From 42a01f59358bd1ac6b8deee5effcb8c6edb0d870 Mon Sep 17 00:00:00 2001 From: Phelipe Lorran <112786693+httpsphl@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:24:46 -0300 Subject: [PATCH 1/2] feat(app): say agents, not bots, in the interface Botloft runs Claude Code, Antigravity and Codex now, so the words in the app's three languages call them agents; "bot" stays only in the name. The place where the old word "agent" meant the engine now says what the setting does ("Runs on", "New agents run on"). --- app/src/App.test.tsx | 24 +++--- app/src/dev/reel.test.tsx | 2 +- app/src/dev/seedChats.ts | 2 +- app/src/dev/seedChief.ts | 2 +- app/src/features/account/account.test.tsx | 4 +- app/src/features/account/archived.test.tsx | 12 +-- app/src/features/account/backup.test.tsx | 2 +- app/src/features/account/cloud.test.tsx | 2 +- app/src/features/account/settings.test.tsx | 30 ++++---- app/src/features/account/tokens.test.tsx | 24 +++--- app/src/features/bots/agentPicker.test.tsx | 16 ++-- app/src/features/bots/avatar.test.tsx | 10 +-- app/src/features/bots/color.test.tsx | 8 +- app/src/features/bots/delete.test.tsx | 42 +++++------ app/src/features/bots/moments.test.tsx | 8 +- app/src/features/bots/panels.test.tsx | 6 +- app/src/features/browser/browser.test.tsx | 10 +-- app/src/features/browser/lesson.test.tsx | 2 +- app/src/features/browser/openForMe.test.tsx | 4 +- app/src/features/browser/tabs.test.tsx | 4 +- app/src/features/browser/window.test.tsx | 4 +- app/src/features/catalog/agency.test.tsx | 46 ++++++------ app/src/features/chat/agentBot.test.tsx | 14 ++-- app/src/features/chat/allowAlways.test.tsx | 2 +- app/src/features/chat/botChange.test.tsx | 6 +- app/src/features/chat/chat.test.tsx | 8 +- app/src/features/chat/context.test.tsx | 4 +- app/src/features/chat/crewAccess.test.tsx | 8 +- app/src/features/chat/effort.test.tsx | 6 +- app/src/features/chat/memory.test.tsx | 4 +- app/src/features/chat/models.test.tsx | 14 ++-- app/src/features/chat/modes.test.tsx | 10 +-- app/src/features/chat/readImage.test.tsx | 4 +- app/src/features/chat/reply.test.tsx | 4 +- app/src/features/chat/sharedFiles.test.tsx | 9 ++- app/src/features/chat/toolGroup.test.tsx | 2 +- .../features/connections/connections.test.tsx | 4 +- app/src/features/crews/botMenu.test.tsx | 20 ++--- app/src/features/crews/chief.test.tsx | 14 ++-- app/src/features/crews/collapse.test.tsx | 6 +- app/src/features/crews/crewMenu.test.tsx | 2 +- app/src/features/crews/crewTemplates.test.tsx | 8 +- app/src/features/crews/preview.test.tsx | 2 +- app/src/features/crews/stateFilter.test.tsx | 2 +- app/src/features/crews/unread.test.tsx | 6 +- app/src/features/desktop/away.test.tsx | 2 +- app/src/features/desktop/desktop.test.tsx | 2 +- app/src/features/desktop/panel.test.tsx | 6 +- app/src/features/desktop/realInput.test.tsx | 2 +- app/src/features/files/files.test.tsx | 4 +- app/src/features/messages/calls.test.tsx | 8 +- app/src/features/messages/messages.test.tsx | 14 ++-- app/src/features/onboarding/agents.test.tsx | 12 +-- app/src/features/onboarding/signIn.test.tsx | 2 +- app/src/features/questions/questions.test.tsx | 4 +- .../features/routines/routineChange.test.tsx | 2 +- .../features/routines/routineRequest.test.tsx | 4 +- app/src/features/routines/routines.test.tsx | 6 +- .../features/routines/routinesPage.test.tsx | 2 +- app/src/features/routines/signals.test.tsx | 12 +-- app/src/features/screens/cursor.test.tsx | 4 +- app/src/features/screens/screens.test.tsx | 2 +- app/src/features/terminal/terminal.test.tsx | 2 +- app/src/features/updates/updates.test.tsx | 4 +- app/src/i18n/en/account.ts | 73 +++++++++--------- app/src/i18n/en/alerts.ts | 4 +- app/src/i18n/en/bots.ts | 53 ++++++------- app/src/i18n/en/browser.ts | 8 +- app/src/i18n/en/catalog.ts | 28 +++---- app/src/i18n/en/catalogBase.ts | 4 +- app/src/i18n/en/catalogDesign.ts | 2 +- app/src/i18n/en/catalogEngineering.ts | 2 +- app/src/i18n/en/catalogProduct.ts | 4 +- app/src/i18n/en/chat.ts | 36 ++++----- app/src/i18n/en/connections.ts | 14 ++-- app/src/i18n/en/crewTemplates.ts | 8 +- app/src/i18n/en/crews.ts | 45 +++++------ app/src/i18n/en/desktop.ts | 4 +- app/src/i18n/en/messages.ts | 13 ++-- app/src/i18n/en/onboarding.ts | 26 +++---- app/src/i18n/en/phone.ts | 24 +++--- app/src/i18n/en/questions.ts | 8 +- app/src/i18n/en/routines.ts | 18 ++--- app/src/i18n/en/search.ts | 4 +- app/src/i18n/en/setup.ts | 4 +- app/src/i18n/en/shell.ts | 8 +- app/src/i18n/en/tools.ts | 4 +- app/src/i18n/en/updates.ts | 2 +- app/src/i18n/es/account.ts | 75 ++++++++++--------- app/src/i18n/es/alerts.ts | 4 +- app/src/i18n/es/bots.ts | 52 ++++++------- app/src/i18n/es/browser.ts | 8 +- app/src/i18n/es/catalog.ts | 28 +++---- app/src/i18n/es/catalogBase.ts | 4 +- app/src/i18n/es/catalogDesign.ts | 2 +- app/src/i18n/es/catalogEngineering.ts | 2 +- app/src/i18n/es/catalogProduct.ts | 4 +- app/src/i18n/es/chat.ts | 37 ++++----- app/src/i18n/es/connections.ts | 14 ++-- app/src/i18n/es/crewTemplates.ts | 8 +- app/src/i18n/es/crews.ts | 44 +++++------ app/src/i18n/es/desktop.ts | 4 +- app/src/i18n/es/messages.ts | 13 ++-- app/src/i18n/es/onboarding.ts | 27 +++---- app/src/i18n/es/phone.ts | 24 +++--- app/src/i18n/es/questions.ts | 8 +- app/src/i18n/es/routines.ts | 18 ++--- app/src/i18n/es/search.ts | 4 +- app/src/i18n/es/setup.ts | 4 +- app/src/i18n/es/shell.ts | 8 +- app/src/i18n/es/tools.ts | 4 +- app/src/i18n/es/updates.ts | 2 +- app/src/i18n/pt-BR/account.ts | 74 +++++++++--------- app/src/i18n/pt-BR/alerts.ts | 4 +- app/src/i18n/pt-BR/bots.ts | 52 ++++++------- app/src/i18n/pt-BR/browser.ts | 8 +- app/src/i18n/pt-BR/catalog.ts | 28 +++---- app/src/i18n/pt-BR/catalogBase.ts | 4 +- app/src/i18n/pt-BR/catalogDesign.ts | 2 +- app/src/i18n/pt-BR/catalogEngineering.ts | 2 +- app/src/i18n/pt-BR/catalogProduct.ts | 4 +- app/src/i18n/pt-BR/chat.ts | 36 ++++----- app/src/i18n/pt-BR/connections.ts | 14 ++-- app/src/i18n/pt-BR/crewTemplates.ts | 8 +- app/src/i18n/pt-BR/crews.ts | 44 +++++------ app/src/i18n/pt-BR/desktop.ts | 4 +- app/src/i18n/pt-BR/messages.ts | 12 +-- app/src/i18n/pt-BR/onboarding.ts | 27 +++---- app/src/i18n/pt-BR/phone.ts | 24 +++--- app/src/i18n/pt-BR/questions.ts | 8 +- app/src/i18n/pt-BR/routines.ts | 18 ++--- app/src/i18n/pt-BR/search.ts | 4 +- app/src/i18n/pt-BR/setup.ts | 4 +- app/src/i18n/pt-BR/shell.ts | 8 +- app/src/i18n/pt-BR/tools.ts | 4 +- app/src/i18n/pt-BR/updates.ts | 2 +- app/src/lib/fakeCatalog.test.ts | 6 +- app/src/lib/fakeMcp.test.ts | 2 +- app/src/lib/tauriTray.test.ts | 4 +- app/src/phone/chats.test.tsx | 20 ++--- app/src/phone/phone.test.tsx | 6 +- app/src/setup/setup.test.tsx | 4 +- app/src/shell/alerts.test.tsx | 6 +- app/src/shell/rail.test.tsx | 2 +- app/src/shell/sidebarToggle.test.tsx | 12 +-- app/src/shell/sounds.test.tsx | 4 +- app/src/shell/tray.test.tsx | 10 +-- app/src/shell/visibility.test.tsx | 2 +- app/src/store/app.test.ts | 8 +- app/src/ui/motion.test.tsx | 4 +- 150 files changed, 907 insertions(+), 889 deletions(-) diff --git a/app/src/App.test.tsx b/app/src/App.test.tsx index 29ebf715..0a06fabc 100644 --- a/app/src/App.test.tsx +++ b/app/src/App.test.tsx @@ -20,11 +20,11 @@ function type(label: string, value: string) { const sidebar = () => screen.getByRole("navigation", { name: "Crews" }); async function newBot(name: string) { - const [headerButton] = await screen.findAllByRole("button", { name: "New bot" }); + const [headerButton] = await screen.findAllByRole("button", { name: "New agent" }); fireEvent.click(headerButton as HTMLElement); type("Name", name); type("Role", `${name} role`); - fireEvent.click(screen.getByRole("button", { name: "Create bot" })); + fireEvent.click(screen.getByRole("button", { name: "Create agent" })); } describe("app", () => { @@ -41,7 +41,7 @@ describe("app", () => { expect(within(sidebar()).getByRole("button", { name: "Ops" })).toBeDefined(); }); - test("a new bot opens with its handle and follows its state", async () => { + test("a new agent opens with its handle and follows its state", async () => { const fake = new FakeBotloft(); fake.addCrew("Ops"); renderApp(fake); @@ -68,7 +68,7 @@ describe("app", () => { expect((await screen.findByRole("alert")).textContent).toBe( "@scout is already used in this crew", ); - expect(screen.getByRole("dialog", { name: "New bot" })).toBeDefined(); + expect(screen.getByRole("dialog", { name: "New agent" })).toBeDefined(); }); test("the app sets itself up on the first run, without asking", async () => { @@ -93,17 +93,17 @@ describe("app", () => { expect(host.installs).toEqual(["install", "install"]); }); - test("archiving a bot asks first", async () => { + test("archiving an agent asks first", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); fake.addBot(crew.id, "Scout"); renderApp(fake); await screen.findByRole("heading", { level: 1, name: "Ops" }); fireEvent.click(within(sidebar()).getByRole("button", { name: /Scout/ })); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); - fireEvent.click(screen.getByRole("menuitem", { name: "Archive bot" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); + fireEvent.click(screen.getByRole("menuitem", { name: "Archive agent" })); const dialog = screen.getByRole("dialog", { name: "Archive Scout?" }); - fireEvent.click(within(dialog).getByRole("button", { name: "Archive bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Archive agent" })); expect(await screen.findByRole("heading", { level: 1, name: "Ops" })).toBeDefined(); expect(within(sidebar()).queryByRole("button", { name: /Scout/ })).toBeNull(); expect(fake.calls.filter((call) => call.method !== "catalog.list").at(-1)?.method).toBe( @@ -122,7 +122,7 @@ describe("app", () => { expect(within(sidebar()).getAllByText("Paused")).toHaveLength(2); }); - test("a missing Claude Code says why bots cannot start and where to get it", async () => { + test("a missing Claude Code says why agents cannot start and where to get it", async () => { const fake = new FakeBotloft(); const host = new FakeHost(); fake.addCrew("Ops"); @@ -132,7 +132,7 @@ describe("app", () => { runtimeError: "claude.exe was not found on PATH.", }; renderApp(fake, host); - const banner = await screen.findByText("Bots can't start"); + const banner = await screen.findByText("Agents can't start"); const alert = banner.closest("[role=alert]") as HTMLElement; expect(alert.textContent).toContain("claude.exe was not found"); fireEvent.click(within(alert).getByRole("button", { name: "How to install Claude Code" })); @@ -147,7 +147,7 @@ describe("app", () => { act(() => fake.setConnection({ kind: "waiting", retryAt: 0 })); expect(screen.getByText("Reconnecting…")).toBeDefined(); }); - test("a bot opens on its chat, with its details in a side panel", async () => { + test("an agent opens on its chat, with its details in a side panel", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout", "Finds sources"); @@ -163,7 +163,7 @@ describe("app", () => { expect(screen.queryByRole("complementary", { name: "About Scout" })).toBeNull(); }); - test("a paused bot still takes messages, and says they wait", async () => { + test("a paused agent still takes messages, and says they wait", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); await fake.call("crews.setPaused", { crewId: crew.id, paused: true }); diff --git a/app/src/dev/reel.test.tsx b/app/src/dev/reel.test.tsx index bbf6cbb5..5ffc1521 100644 --- a/app/src/dev/reel.test.tsx +++ b/app/src/dev/reel.test.tsx @@ -8,7 +8,7 @@ afterEach(() => { vi.useRealTimers(); }); -test("the bot in the middle lands, works, cheers, then the row moves on", () => { +test("the agent in the middle lands, works, cheers, then the row moves on", () => { const { container } = render(); const middle = () => container.querySelector("[data-center] svg") as SVGElement; const color = () => middle().querySelector("rect")?.getAttribute("fill"); diff --git a/app/src/dev/seedChats.ts b/app/src/dev/seedChats.ts index b0604c58..441b84d2 100644 --- a/app/src/dev/seedChats.ts +++ b/app/src/dev/seedChats.ts @@ -174,7 +174,7 @@ export function seedChats(fake: FakeBotloft, crew: Crew): void { to: crew.reviewer, body: "Two of the links in the draft moved; the new ones are in shared/links.md.", }); - talk.deliver(late.delivery.id, "dead", "the bot did not start in time"); + talk.deliver(late.delivery.id, "dead", "the agent did not start in time"); at(30); void fake.call("messages.send", { diff --git a/app/src/dev/seedChief.ts b/app/src/dev/seedChief.ts index 1e7ceac0..c5b512bd 100644 --- a/app/src/dev/seedChief.ts +++ b/app/src/dev/seedChief.ts @@ -27,7 +27,7 @@ export function seedChief(fake: FakeBotloft): void { workFolder: "D:\\Projects\\Bakery site", lead: { name: "Chief", - role: "Leads the crew: plans the work, suggests new bots and hands out tasks", + role: "Leads the crew: plans the work, suggests new agents and hands out tasks", instructions: "Build and keep up the website of my bakery.", }, }) diff --git a/app/src/features/account/account.test.tsx b/app/src/features/account/account.test.tsx index 2bcb39db..a6e33daa 100644 --- a/app/src/features/account/account.test.tsx +++ b/app/src/features/account/account.test.tsx @@ -81,11 +81,11 @@ describe("account area", () => { expect(dialog.textContent).toContain("92% used"); }); - test("usage waits for a bot's first reply", async () => { + test("usage waits for an agent's first reply", async () => { renderApp(); await openAccount(); fireEvent.click(screen.getByRole("menuitem", { name: "Usage" })); - expect(screen.getByText("Usage shows up after a bot's first reply.")).toBeDefined(); + expect(screen.getByText("Usage shows up after an agent's first reply.")).toBeDefined(); }); test("settings change the theme and the language", async () => { diff --git a/app/src/features/account/archived.test.tsx b/app/src/features/account/archived.test.tsx index ed8fff3d..28b8d6e2 100644 --- a/app/src/features/account/archived.test.tsx +++ b/app/src/features/account/archived.test.tsx @@ -37,15 +37,15 @@ describe("settings, archived", () => { const list = await within(dialog).findByRole("list", { name: "Archived" }); const rows = within(list).getAllByRole("listitem"); expect(rows.map((row) => row.textContent)).toEqual([ - "DocsCrew · 1 bot · archived nowDelete", - "ScoutBot in Ops · archived nowDelete", + "DocsCrew · 1 agent · archived nowDelete", + "ScoutAgent in Ops · archived nowDelete", ]); // A bot of an archived crew goes with its crew, not on its own. expect(within(list).queryByText("Editor")).toBeNull(); expect(dialog.textContent).toContain("Botloft still keeps their conversations"); }); - test("deletes an archived bot, saying it leaves rather than stops", async () => { + test("deletes an archived agent, saying it leaves rather than stops", async () => { const { fake, scout } = await someArchived(); const dialog = await openArchived(); fireEvent.click(await within(dialog).findByRole("button", { name: "Delete Scout" })); @@ -53,19 +53,19 @@ describe("settings, archived", () => { expect(confirm.textContent).toContain("Scout leaves Botloft for good"); expect(confirm.textContent).not.toContain("stops now"); expect(within(confirm).getByText(scout.workspace)).toBeDefined(); - fireEvent.click(within(confirm).getByRole("button", { name: "Delete bot" })); + fireEvent.click(within(confirm).getByRole("button", { name: "Delete agent" })); await waitFor(() => expect(within(dialog).queryByText("Scout")).toBeNull()); expect(fake.bots.has(scout.id)).toBe(false); expect(within(dialog).getByText("Docs")).toBeDefined(); }); - test("deletes an archived crew with its bots, then has nothing left", async () => { + test("deletes an archived crew with its agents, then has nothing left", async () => { const { fake, docs, scout, editor } = await someArchived(); await fake.call("bots.delete", { botId: scout.id }); const dialog = await openArchived(); fireEvent.click(await within(dialog).findByRole("button", { name: "Delete Docs" })); const confirm = screen.getByRole("dialog", { name: "Delete Docs?" }); - expect(confirm.textContent).toContain("Docs and its bot leave Botloft for good"); + expect(confirm.textContent).toContain("Docs and its agent leave Botloft for good"); expect(within(confirm).getByText(docs.workFolder)).toBeDefined(); fireEvent.click(within(confirm).getByRole("button", { name: "Delete crew" })); expect(await within(dialog).findByText("Nothing is archived.")).toBeDefined(); diff --git a/app/src/features/account/backup.test.tsx b/app/src/features/account/backup.test.tsx index 4a0801a8..3a79a243 100644 --- a/app/src/features/account/backup.test.tsx +++ b/app/src/features/account/backup.test.tsx @@ -57,7 +57,7 @@ describe("backup", () => { type(field, FAKE_PASSPHRASE); fireEvent.click(within(dialog).getByRole("button", { name: "Open" })); - expect(await within(dialog).findByText("Ops: 2 bots")).toBeDefined(); + expect(await within(dialog).findByText("Ops: 2 agents")).toBeDefined(); fireEvent.click(within(dialog).getByRole("button", { name: "Restore and restart" })); await waitFor(() => expect(host.installs).toContain("restart")); expect(fake.calls.some((each) => each.method === "backup.confirm")).toBe(true); diff --git a/app/src/features/account/cloud.test.tsx b/app/src/features/account/cloud.test.tsx index 348f058b..949bbb70 100644 --- a/app/src/features/account/cloud.test.tsx +++ b/app/src/features/account/cloud.test.tsx @@ -88,7 +88,7 @@ describe("the account and the copies in the cloud", () => { const field = await within(dialog).findByLabelText("The copy's passphrase"); type(field, FAKE_PASSPHRASE); fireEvent.click(within(dialog).getByRole("button", { name: "Open" })); - expect(await within(dialog).findByText("Ops: 2 bots")).toBeDefined(); + expect(await within(dialog).findByText("Ops: 2 agents")).toBeDefined(); expect(within(dialog).getByText(/The chats and files are not in it/)).toBeDefined(); fireEvent.click(within(dialog).getByRole("button", { name: "Restore and restart" })); await waitFor(() => expect(host.installs).toContain("restart")); diff --git a/app/src/features/account/settings.test.tsx b/app/src/features/account/settings.test.tsx index 739ed78c..e3782c83 100644 --- a/app/src/features/account/settings.test.tsx +++ b/app/src/features/account/settings.test.tsx @@ -33,7 +33,7 @@ describe("settings", () => { }); const start = within(dialog).getByRole("switch", { name: `Start with ${SYSTEM}` }); const awake = within(dialog).getByRole("switch", { - name: "Keep the computer awake while bots work", + name: "Keep the computer awake while agents work", }); await waitFor(() => expect(start.hasAttribute("disabled")).toBe(false)); expect([keep, start, awake].map((s) => s.getAttribute("aria-checked"))).toEqual([ @@ -41,16 +41,16 @@ describe("settings", () => { "true", "true", ]); - expect(dialog.textContent).toContain("Your bots go on working and answering"); + expect(dialog.textContent).toContain("Your agents go on working and answering"); fireEvent.click(keep); expect(keep.getAttribute("aria-checked")).toBe("false"); - expect(dialog.textContent).toContain("Closing Botloft stops every bot."); + expect(dialog.textContent).toContain("Closing Botloft stops every agent."); expect(localStorage.getItem("botloft.whenClosed")).toBe("stop"); fireEvent.click(start); expect(start.getAttribute("aria-checked")).toBe("false"); - expect(dialog.textContent).toContain("the bots wait until you open Botloft"); + expect(dialog.textContent).toContain("the agents wait until you open Botloft"); fireEvent.click(awake); await waitFor(() => expect(fake.settings).toEqual({ @@ -95,12 +95,12 @@ describe("settings", () => { fireEvent.click(within(dialog).getByRole("tab", { name: "Notifications" })); const on = (name: string) => within(dialog).getByRole("switch", { name }).getAttribute("aria-checked"); - expect([on("When a bot needs you"), on("When a bot finishes"), on("Play a sound")]).toEqual([ - "true", - "false", - "true", - ]); - fireEvent.click(within(dialog).getByRole("switch", { name: "When a bot finishes" })); + expect([ + on("When an agent needs you"), + on("When an agent finishes"), + on("Play a sound"), + ]).toEqual(["true", "false", "true"]); + fireEvent.click(within(dialog).getByRole("switch", { name: "When an agent finishes" })); expect(prefs.notifyDone.get()).toBe(true); expect(dialog.textContent).not.toContain("notifications only come"); act(() => prefs.tray.set(false)); @@ -136,7 +136,7 @@ describe("settings", () => { }); describe("closing the window", () => { - test("leaves the bots working: near the clock, or closed without the icon", async () => { + test("leaves the agents working: near the clock, or closed without the icon", async () => { const { host } = renderApp(); await screen.findByRole("heading", { name: "Welcome to Botloft" }); await waitFor(() => expect(host.tray).not.toBeNull()); @@ -147,7 +147,7 @@ describe("closing the window", () => { expect([host.closed, host.stops]).toEqual([true, 0]); }); - test("stops the bots, with the window out of sight first, when chosen", async () => { + test("stops the agents, with the window out of sight first, when chosen", async () => { const { host } = renderApp(); await screen.findByRole("heading", { name: "Welcome to Botloft" }); prefs.whenClosed.set("stop"); @@ -157,7 +157,7 @@ describe("closing the window", () => { expect(host.closed).toBe(true); }); - test("opening Botloft again starts the bots it stopped", async () => { + test("opening Botloft again starts the agents it stopped", async () => { prefs.whenClosed.set("stop"); const host = new FakeHost(); host.status = { state: "stopped", port: 45710, home: "C:\\data" }; @@ -168,7 +168,7 @@ describe("closing the window", () => { finish = () => resolve(installed as never); }); renderApp(new FakeBotloft(), host); - expect(await screen.findByText("Starting your bots…")).toBeDefined(); + expect(await screen.findByText("Starting your agents…")).toBeDefined(); expect(screen.queryByText(/In Settings you choose/)).toBeNull(); await act(async () => finish()); expect(await screen.findByText(/Running while this window is open/)).toBeDefined(); @@ -180,7 +180,7 @@ describe("closing the window", () => { renderApp(fake); expect( await screen.findByText( - "Running in the background, so your bots keep working after you close this window.", + "Running in the background, so your agents keep working after you close this window.", ), ).toBeDefined(); }); diff --git a/app/src/features/account/tokens.test.tsx b/app/src/features/account/tokens.test.tsx index 766734a8..2822afa3 100644 --- a/app/src/features/account/tokens.test.tsx +++ b/app/src/features/account/tokens.test.tsx @@ -16,8 +16,8 @@ async function openUsage(fake: FakeBotloft) { return screen.getByRole("dialog", { name: "Usage" }); } -describe("tokens by bot", () => { - test("each bot's tokens add up over the period chosen, with the total", async () => { +describe("tokens by agent", () => { + test("each agent's tokens add up over the period chosen, with the total", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout", "Finds sources"); @@ -31,19 +31,19 @@ describe("tokens by bot", () => { fake.chat.turn(writer.id); const dialog = await openUsage(fake); - const list = await within(dialog).findByRole("list", { name: "Use by bot" }); + const list = await within(dialog).findByRole("list", { name: "Use by agent" }); const rows = within(list).getAllByRole("listitem"); // Each turn: 12 new, 1,800 into the cache and 640 written count; 24,000 reread do not. expect(rows.map((row) => row.textContent)).toEqual([ "Scout4.9kOps · Worked 2 times", "Writer2.5kOps · Worked once", - "All bots7.4kWorked 3 times", + "All agents7.4kWorked 3 times", ]); fireEvent.click(within(dialog).getByRole("radio", { name: "7 days" })); expect(await within(dialog).findByText("4.9k")).toBeDefined(); expect(dialog.textContent).toContain("Writer4.9kOps · Worked 2 times"); - expect(dialog.textContent).toContain("All bots9.8k"); + expect(dialog.textContent).toContain("All agents9.8k"); }); test("the conversation sent again after a pause counts in the total", async () => { @@ -60,7 +60,7 @@ describe("tokens by bot", () => { fake.chat.turn(scout.id); const dialog = await openUsage(fake); - const list = await within(dialog).findByRole("list", { name: "Use by bot" }); + const list = await within(dialog).findByRole("list", { name: "Use by agent" }); expect( within(list) .getAllByRole("listitem") @@ -68,7 +68,7 @@ describe("tokens by bot", () => { ).toEqual(["Scout76.1kOps · Worked 2 times"]); }); - test("bots with the same name in two crews show their crew", async () => { + test("agents with the same name in two crews show their crew", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const news = fake.addCrew("Newsroom"); @@ -76,7 +76,7 @@ describe("tokens by bot", () => { fake.chat.turn(fake.addBot(news.id, "Scout", "Finds stories").id); const dialog = await openUsage(fake); - const list = await within(dialog).findByRole("list", { name: "Use by bot" }); + const list = await within(dialog).findByRole("list", { name: "Use by agent" }); expect( within(list) .getAllByRole("listitem") @@ -84,13 +84,13 @@ describe("tokens by bot", () => { ).toEqual([ "Scout2.5kNewsroom · Worked once", "Scout2.5kOps · Worked once", - "All bots4.9kWorked 2 times", + "All agents4.9kWorked 2 times", ]); }); test("a period with no work says so", async () => { const dialog = await openUsage(new FakeBotloft()); - expect(await within(dialog).findByText("No bot worked in this period.")).toBeDefined(); + expect(await within(dialog).findByText("No agent worked in this period.")).toBeDefined(); }); test("today starts at midnight; the others count back from now", () => { @@ -101,7 +101,7 @@ describe("tokens by bot", () => { }); }); -describe("each bot's share of the weekly plan", () => { +describe("each agent's share of the weekly plan", () => { test("shows once learned, with the tokens beside it, and says it is an estimate", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); @@ -112,7 +112,7 @@ describe("each bot's share of the weekly plan", () => { fake.planSharePerToken = 0.000_001; const dialog = await openUsage(fake); - const list = await within(dialog).findByRole("list", { name: "Use by bot" }); + const list = await within(dialog).findByRole("list", { name: "Use by agent" }); expect(within(list).getByRole("listitem").textContent).toBe( "Scout≈ 0.5% of the weekOps · Worked 2 times · 4.9k tokens", ); diff --git a/app/src/features/bots/agentPicker.test.tsx b/app/src/features/bots/agentPicker.test.tsx index c15d00c9..890813c6 100644 --- a/app/src/features/bots/agentPicker.test.tsx +++ b/app/src/features/bots/agentPicker.test.tsx @@ -11,21 +11,21 @@ async function openNewBot(agents: ("claude" | "agy")[]) { fake.addCrew("Ops"); renderApp(fake); await crewOpened("Ops"); - const [button] = await screen.findAllByRole("button", { name: "New bot" }); + const [button] = await screen.findAllByRole("button", { name: "New agent" }); fireEvent.click(button as HTMLElement); - return within(await screen.findByRole("dialog", { name: "New bot" })); + return within(await screen.findByRole("dialog", { name: "New agent" })); } -describe("choosing the agent of a new bot", () => { +describe("choosing the agent of a new agent", () => { test("with only Claude Code there is no choice to make", async () => { const dialog = await openNewBot(["claude"]); - expect(dialog.queryByLabelText("Agent")).toBeNull(); + expect(dialog.queryByLabelText("Runs on")).toBeNull(); expect(dialog.getByLabelText("Model")).toBeDefined(); }); test("an experimental agent comes with its warning and no model choice", async () => { const dialog = await openNewBot(["claude", "agy"]); - fireEvent.change(dialog.getByLabelText("Agent"), { target: { value: "agy" } }); + fireEvent.change(dialog.getByLabelText("Runs on"), { target: { value: "agy" } }); expect(dialog.getByRole("note").textContent).toMatch(/cannot ask you/); expect(dialog.queryByLabelText("Model")).toBeNull(); }); @@ -38,10 +38,10 @@ describe("Codex in the new-bot dialog", () => { fake.addCrew("Ops"); renderApp(fake); await crewOpened("Ops"); - const [button] = await screen.findAllByRole("button", { name: "New bot" }); + const [button] = await screen.findAllByRole("button", { name: "New agent" }); fireEvent.click(button as HTMLElement); - const dialog = within(await screen.findByRole("dialog", { name: "New bot" })); - fireEvent.change(dialog.getByLabelText("Agent"), { target: { value: "codex" } }); + const dialog = within(await screen.findByRole("dialog", { name: "New agent" })); + fireEvent.change(dialog.getByLabelText("Runs on"), { target: { value: "codex" } }); expect(dialog.getByRole("note").textContent).toMatch( /asks you before it changes a file or runs a command/, ); diff --git a/app/src/features/bots/avatar.test.tsx b/app/src/features/bots/avatar.test.tsx index ea96dbbc..378f15a5 100644 --- a/app/src/features/bots/avatar.test.tsx +++ b/app/src/features/bots/avatar.test.tsx @@ -63,7 +63,7 @@ describe("mascot", () => { } }); - test("only a working bot's mascot moves on and on", () => { + test("only a working agent's mascot moves on and on", () => { const forever = [...moodRules.matchAll(/([^{}]+)\{[^{}]*infinite[^{}]*\}/g)].map((rule) => (rule[1] ?? "").trim(), ); @@ -99,7 +99,7 @@ describe("mascot", () => { expect(used).toEqual(clips.map((id) => `url(#${id})`)); }); - test("the mood drives the motion, and only a working bot throws embers", () => { + test("the mood drives the motion, and only a working agent throws embers", () => { const { container, rerender } = render(); const svg = () => container.querySelector("svg"); expect(svg()?.dataset.mood).toBe("idle"); @@ -112,7 +112,7 @@ describe("mascot", () => { expect(svg()?.dataset.mood).toBeUndefined(); }); - test("cheers once when its bot finishes, not when it shows up idle", () => { + test("cheers once when its agent finishes, not when it shows up idle", () => { vi.useFakeTimers(); try { const { container, rerender } = render(); @@ -174,7 +174,7 @@ describe("state badge", () => { expect(container.querySelector(".state-trace")?.getAttribute("pathLength")).toBe("100"); }); - test("fades in when the bot starts working, not when it was already working", () => { + test("fades in when the agent starts working, not when it was already working", () => { const { container, rerender } = render( , ); @@ -193,7 +193,7 @@ describe("ListAvatar", () => { paused: false, }); - test("shows the bot's mood: idle, stopped or working", () => { + test("shows the agent's mood: idle, stopped or working", () => { const { container, rerender } = render( , ); diff --git a/app/src/features/bots/color.test.tsx b/app/src/features/bots/color.test.tsx index 10916d52..16fa0fcf 100644 --- a/app/src/features/bots/color.test.tsx +++ b/app/src/features/bots/color.test.tsx @@ -26,7 +26,7 @@ describe("color space", () => { }); }); -describe("picking a bot's color", () => { +describe("picking an agent's color", () => { async function editScout() { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); @@ -35,7 +35,7 @@ describe("picking a bot's color", () => { await crewOpened("Ops"); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); fireEvent.click(screen.getByRole("menuitem", { name: "Edit" })); return { fake, scout, dialog: screen.getByRole("dialog", { name: "Edit Scout" }) }; } @@ -69,7 +69,7 @@ describe("picking a bot's color", () => { await waitFor(() => expect(fake.bots.get(scout.id)?.color).toBe("#0080FF")); }); - test("a bot with a color outside the palette opens with the picker", async () => { + test("an agent with a color outside the palette opens with the picker", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); @@ -78,7 +78,7 @@ describe("picking a bot's color", () => { await crewOpened("Ops"); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); fireEvent.click(screen.getByRole("menuitem", { name: "Edit" })); const dialog = screen.getByRole("dialog", { name: "Edit Scout" }); expect((within(dialog).getByLabelText("Hex") as HTMLInputElement).value).toBe("#123ABC"); diff --git a/app/src/features/bots/delete.test.tsx b/app/src/features/bots/delete.test.tsx index 8bf15e40..996200b5 100644 --- a/app/src/features/bots/delete.test.tsx +++ b/app/src/features/bots/delete.test.tsx @@ -26,13 +26,13 @@ const inSidebar = (name: string) => /** Picks Delete in the open bot's menu and returns the confirmation. */ function askToDelete(name: string) { - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); - fireEvent.click(screen.getByRole("menuitem", { name: "Delete bot" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); + fireEvent.click(screen.getByRole("menuitem", { name: "Delete agent" })); return screen.getByRole("dialog", { name: `Delete ${name}?` }); } -describe("deleting a bot", () => { - test("says what goes and what stays, then removes the bot", async () => { +describe("deleting an agent", () => { + test("says what goes and what stays, then removes the agent", async () => { const { fake, scout } = await twoBots(); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); @@ -44,7 +44,7 @@ describe("deleting a bot", () => { expect(within(dialog).getByText(scout.workspace)).toBeDefined(); expect(dialog.textContent).not.toContain("without a chief"); - fireEvent.click(within(dialog).getByRole("button", { name: "Delete bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Delete agent" })); // Back on the crew's page, without the bot. await waitFor(() => expect(inSidebar("Scout")).toBeNull()); expect(screen.getByRole("heading", { level: 1, name: "Ops" })).toBeDefined(); @@ -53,7 +53,7 @@ describe("deleting a bot", () => { expect(inSidebar("Writer")).not.toBeNull(); }); - test("cancelling keeps the bot", async () => { + test("cancelling keeps the agent", async () => { const { fake, scout } = await twoBots(); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); @@ -64,17 +64,17 @@ describe("deleting a bot", () => { expect(fake.calls.some((call) => call.method === "bots.delete")).toBe(false); }); - test("works from the right-click menu, for a bot that is not open", async () => { + test("works from the right-click menu, for an agent that is not open", async () => { const { fake, scout } = await twoBots(); openBot("Writer"); await screen.findByRole("heading", { level: 1, name: "Writer" }); fireEvent.contextMenu(inSidebar("Scout") as HTMLElement, { clientX: 80, clientY: 120 }); const menu = screen.getByRole("menu", { name: "Actions for Scout" }); // Delete comes last, after Archive. - expect(within(menu).getAllByRole("menuitem").at(-1)?.textContent).toBe("Delete bot"); - fireEvent.click(within(menu).getByRole("menuitem", { name: "Delete bot" })); + expect(within(menu).getAllByRole("menuitem").at(-1)?.textContent).toBe("Delete agent"); + fireEvent.click(within(menu).getByRole("menuitem", { name: "Delete agent" })); const dialog = screen.getByRole("dialog", { name: "Delete Scout?" }); - fireEvent.click(within(dialog).getByRole("button", { name: "Delete bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Delete agent" })); await waitFor(() => expect(inSidebar("Scout")).toBeNull()); expect(fake.bots.has(scout.id)).toBe(false); expect(screen.getByRole("heading", { level: 1, name: "Writer" })).toBeDefined(); @@ -89,26 +89,26 @@ describe("deleting a bot", () => { expect(dialog.textContent).toContain("Ops will be left without a chief."); }); - test("a delete that fails says so and keeps the bot", async () => { + test("a delete that fails says so and keeps the agent", async () => { const { fake, scout } = await twoBots(); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); const dialog = askToDelete("Scout"); fake.failNext("bots.delete", new RpcError(-32603, "internal error")); - fireEvent.click(within(dialog).getByRole("button", { name: "Delete bot" })); - await screen.findByText(/Could not delete the bot/); + fireEvent.click(within(dialog).getByRole("button", { name: "Delete agent" })); + await screen.findByText(/Could not delete the agent/); expect(fake.bots.has(scout.id)).toBe(true); expect(inSidebar("Scout")).not.toBeNull(); }); - test("what it sent stays in the timeline, from a bot no longer in the crew", async () => { + test("what it sent stays in the timeline, from an agent no longer in the crew", async () => { const { fake, scout, writer } = await twoBots(); fake.conversation.say({ from: scout.id, to: writer.id, body: "The draft is in shared/." }); await fake.call("bots.delete", { botId: scout.id }); openTab("Timeline"); const timeline = screen.getByRole("tabpanel", { name: "Timeline" }); const message = (await within(timeline).findByText("The draft is in shared/.")).closest("li"); - expect(message?.textContent).toContain("a bot no longer in the crew"); + expect(message?.textContent).toContain("an agent no longer in the crew"); }); }); @@ -119,7 +119,7 @@ describe("deleting a crew", () => { return screen.getByRole("dialog", { name: `Delete ${name}?` }); } - test("says what goes and what stays, then removes the crew and its bots", async () => { + test("says what goes and what stays, then removes the crew and its agents", async () => { const { fake, crew, scout } = await twoBots(); const docs = fake.addCrew("Docs"); await waitFor(() => @@ -127,7 +127,7 @@ describe("deleting a crew", () => { ); const dialog = askToDeleteCrew("Ops"); expect(dialog.textContent).toContain( - "Ops and its 2 bots stop now and leave Botloft for good, with their conversations, routines and tasks. This can't be undone.", + "Ops and its 2 agents stop now and leave Botloft for good, with their conversations, routines and tasks. This can't be undone.", ); expect(dialog.textContent).toContain("The folders stay on your computer"); expect(within(dialog).getByText(crew.workFolder)).toBeDefined(); @@ -154,7 +154,7 @@ describe("deleting a crew", () => { fake.addBot(crew.id, "Scout"); await waitFor(() => expect(inSidebar("Scout")).not.toBeNull()); - expect(askToDeleteCrew("Ops").textContent).toContain("Ops and its bot stop now"); + expect(askToDeleteCrew("Ops").textContent).toContain("Ops and its agent stop now"); }); test("the last crew gone, the app is back at the start", async () => { @@ -170,7 +170,7 @@ describe("sending the folder to the Recycle Bin", () => { const box = (dialog: HTMLElement, name: string) => within(dialog).getByRole("checkbox", { name }) as HTMLInputElement; - test("is off until the owner asks, then the bot's folder goes with it", async () => { + test("is off until the owner asks, then the agent's folder goes with it", async () => { const { fake, scout } = await twoBots(); openBot("Scout"); await screen.findByRole("heading", { level: 1, name: "Scout" }); @@ -185,7 +185,7 @@ describe("sending the folder to the Recycle Bin", () => { expect(dialog.textContent).not.toContain("stays on your computer"); expect(within(dialog).getByText(scout.workspace)).toBeDefined(); - fireEvent.click(within(dialog).getByRole("button", { name: "Delete bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Delete agent" })); await waitFor(() => expect(inSidebar("Scout")).toBeNull()); expect(lastCall(fake)).toEqual({ method: "bots.delete", @@ -201,7 +201,7 @@ describe("sending the folder to the Recycle Bin", () => { await screen.findByRole("heading", { level: 1, name: "Scout" }); const dialog = askToDelete("Scout"); fireEvent.click(box(dialog, "Move this folder to the Recycle Bin")); - fireEvent.click(within(dialog).getByRole("button", { name: "Delete bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Delete agent" })); const notice = await screen.findByText(/did not go to the Recycle Bin/); expect(notice.textContent).toBe( diff --git a/app/src/features/bots/moments.test.tsx b/app/src/features/bots/moments.test.tsx index 2013dde5..3a3eadf5 100644 --- a/app/src/features/bots/moments.test.tsx +++ b/app/src/features/bots/moments.test.tsx @@ -20,7 +20,7 @@ afterEach(() => { const svgOf = (container: HTMLElement) => container.querySelector("svg") as SVGSVGElement; describe("moments", () => { - test("wakes when its bot comes back, not when it shows up awake", () => { + test("wakes when its agent comes back, not when it shows up awake", () => { vi.useFakeTimers(); const { container, rerender } = render(); expect(svgOf(container).hasAttribute("data-wake")).toBe(false); @@ -72,7 +72,7 @@ describe("moments", () => { expect(svgOf(container).hasAttribute("data-cheer")).toBe(true); }); - test("a new bot that starts up while it pops in does not wake on top of it", () => { + test("a new agent that starts up while it pops in does not wake on top of it", () => { vi.useFakeTimers(); vi.setSystemTime(APP_OPENED + 60_000); const created = { id: "bot_new" as BotId, color: "#9be564", paused: false }; @@ -91,7 +91,7 @@ describe("moments", () => { expect(svgOf(container).hasAttribute("data-wake")).toBe(false); }); - test("a bot created a moment ago pops in once; an older one does not", () => { + test("an agent created a moment ago pops in once; an older one does not", () => { vi.useFakeTimers(); vi.setSystemTime(APP_OPENED + 60_000); const bot = (createdAt: number) => ({ @@ -132,7 +132,7 @@ describe("moments", () => { }); describe("glances", () => { - test("two bots look at each other when one writes to the other", async () => { + test("two agents look at each other when one writes to the other", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); diff --git a/app/src/features/bots/panels.test.tsx b/app/src/features/bots/panels.test.tsx index 25db6aa6..cd3d807e 100644 --- a/app/src/features/bots/panels.test.tsx +++ b/app/src/features/bots/panels.test.tsx @@ -31,8 +31,8 @@ async function started(fake: FakeBotloft) { const panel = (name: string) => screen.queryByRole("complementary", { name }); const button = (name: RegExp) => screen.getByRole("button", { name }); -describe("the panel beside a bot's chat", () => { - test("comes back with the bot, each bot with its own", async () => { +describe("the panel beside an agent's chat", () => { + test("comes back with the agent, each agent with its own", async () => { const { fake, scout } = crew(); await started(fake); fireEvent.click(button(/^Show browser/)); @@ -92,7 +92,7 @@ describe("the panel beside a bot's chat", () => { expect(panel("Scout's screens")).not.toBeNull(); }); - test("what the bot did while the owner was away stays behind its button, beside the panel that came back", async () => { + test("what the agent did while the owner was away stays behind its button, beside the panel that came back", async () => { const { fake, scout } = crew(); await started(fake); fireEvent.click(button(/^Show files/)); diff --git a/app/src/features/browser/browser.test.tsx b/app/src/features/browser/browser.test.tsx index a02b3f00..97182caf 100644 --- a/app/src/features/browser/browser.test.tsx +++ b/app/src/features/browser/browser.test.tsx @@ -29,7 +29,7 @@ const toggle = () => screen.getByRole("button", { name: /Show browser|Hide brows const panel = () => screen.getByRole("complementary", { name: "Scout's browser" }); describe("browser panel", () => { - test("watches the bot's browser while open, and says when it has not opened one", async () => { + test("watches the agent's browser while open, and says when it has not opened one", async () => { const { fake, scout } = crew(); await openScout(fake); fireEvent.click(toggle()); @@ -41,7 +41,7 @@ describe("browser panel", () => { expect(fake.browser.watches).toEqual([scout.id, null]); }); - test("shows the live page, its address, and the bot's cursor where it clicks", async () => { + test("shows the live page, its address, and the agent's cursor where it clicks", async () => { const { fake, scout } = crew(); fake.browser.open(scout.id, "https://example.com/signin", "Sign in"); await openScout(fake); @@ -135,7 +135,7 @@ describe("browser panel", () => { expect(fake.browser.state(scout.id).resting).toBe(false); }); - test("closes when the browser rests and opens again when the bot wakes it, but not out of the owner's hands", async () => { + test("closes when the browser rests and opens again when the agent wakes it, but not out of the owner's hands", async () => { const { fake, scout } = crew(); await openScout(fake); act(() => { @@ -169,7 +169,7 @@ describe("browser panel", () => { expect(toggle().getAttribute("aria-label")).toBe("Hide browser"); }); - test("opens by itself when the bot starts browsing, and the button marks it once closed", async () => { + test("opens by itself when the agent starts browsing, and the button marks it once closed", async () => { const { fake, scout } = crew(); await openScout(fake); fireEvent.click(screen.getByRole("button", { name: "Show files" })); @@ -190,7 +190,7 @@ describe("browser panel", () => { expect(screen.queryByRole("complementary", { name: "Scout's browser" })).toBeNull(); }); - test("a browser already open when the bot is opened stays behind its button", async () => { + test("a browser already open when the agent is opened stays behind its button", async () => { const { fake, scout } = crew(); fake.browser.open(scout.id, "https://example.com/", "Example"); await openScout(fake); diff --git a/app/src/features/browser/lesson.test.tsx b/app/src/features/browser/lesson.test.tsx index ea531873..9f6f63f1 100644 --- a/app/src/features/browser/lesson.test.tsx +++ b/app/src/features/browser/lesson.test.tsx @@ -78,7 +78,7 @@ describe("teaching a task", () => { ); }); - test("a lesson records live and goes to the bot to remember, without the steps taken out", async () => { + test("a lesson records live and goes to the agent to remember, without the steps taken out", async () => { const { fake, scout } = await inHands(); const dialog = await finish(fake, scout.id); fireEvent.click(within(dialog).getByRole("button", { name: "Remove step 2" })); diff --git a/app/src/features/browser/openForMe.test.tsx b/app/src/features/browser/openForMe.test.tsx index dbee1f43..6b2c3a68 100644 --- a/app/src/features/browser/openForMe.test.tsx +++ b/app/src/features/browser/openForMe.test.tsx @@ -7,8 +7,8 @@ afterEach(cleanup); const panel = () => screen.getByRole("complementary", { name: "Scout's browser" }); -describe("the owner opens the bot's closed browser", () => { - test("on its last page, in their hands, without telling the bot", async () => { +describe("the owner opens the agent's closed browser", () => { + test("on its last page, in their hands, without telling the agent", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout", "Finds sources"); diff --git a/app/src/features/browser/tabs.test.tsx b/app/src/features/browser/tabs.test.tsx index 349076d1..c0379cc1 100644 --- a/app/src/features/browser/tabs.test.tsx +++ b/app/src/features/browser/tabs.test.tsx @@ -42,8 +42,8 @@ const address = () => within(panel()).getByRole("textbox", { name: "Address" }) const called = (fake: FakeBotloft, method: string) => fake.calls.filter((call) => call.method === method).map((call) => call.params); -describe("the tabs of the bot's browser", () => { - test("show what the bot has open, by title and site, and only show until the owner takes it", async () => { +describe("the tabs of the agent's browser", () => { + test("show what the agent has open, by title and site, and only show until the owner takes it", async () => { const { fake, scout } = crew(); await openPanel(fake, scout.id); expect(selected()).toEqual([ diff --git a/app/src/features/browser/window.test.tsx b/app/src/features/browser/window.test.tsx index 296c7468..f30c9b4e 100644 --- a/app/src/features/browser/window.test.tsx +++ b/app/src/features/browser/window.test.tsx @@ -27,7 +27,7 @@ async function openScout() { const panel = () => screen.getByRole("complementary", { name: "Scout's browser" }); describe("the browser in a window of its own", () => { - test("opens for signing in, says the bot waits, and goes back when the window closes", async () => { + test("opens for signing in, says the agent waits, and goes back when the window closes", async () => { const { fake, scout } = await openScout(); // Why is folded until the owner asks. expect(within(panel()).queryByText(/like Google/)).toBeNull(); @@ -57,7 +57,7 @@ describe("the browser in a window of its own", () => { expect(within(panel()).getByRole("button", { name: "Sign in in a window" })).toBeDefined(); }); - test("is offered when the bot asks for a hand", async () => { + test("is offered when the agent asks for a hand", async () => { const { fake, scout } = await openScout(); act(() => { fake.browser.ask(scout.id, "Sign in to your Google account"); diff --git a/app/src/features/catalog/agency.test.tsx b/app/src/features/catalog/agency.test.tsx index 5ccc2e5b..f3e588b3 100644 --- a/app/src/features/catalog/agency.test.tsx +++ b/app/src/features/catalog/agency.test.tsx @@ -24,21 +24,21 @@ async function openCrewWithChief() { const addCalls = (fake: FakeBotloft) => fake.calls.filter((call) => call.method === "catalog.add").map((call) => call.params); -describe("the Bot agency", () => { - test("the invitation shows a few bots, and a button for all of them", async () => { +describe("the Agent agency", () => { + test("the invitation shows a few agents, and a button for all of them", async () => { await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Bot agency" }); + const invite = await screen.findByRole("region", { name: "Agent agency" }); expect(await within(invite).findAllByRole("listitem")).toHaveLength(8); expect(within(invite).queryByRole("searchbox")).toBeNull(); - fireEvent.click(within(invite).getByRole("button", { name: "See all bots" })); - const dialog = await screen.findByRole("dialog", { name: "Bot agency" }); + fireEvent.click(within(invite).getByRole("button", { name: "See all agents" })); + const dialog = await screen.findByRole("dialog", { name: "Agent agency" }); expect(await within(dialog).findAllByRole("listitem")).toHaveLength(129); }); - test("a crew with only its chief invites the owner to pick bots, and a click adds one", async () => { + test("a crew with only its chief invites the owner to pick agents, and a click adds one", async () => { const fake = await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Bot agency" }); + const invite = await screen.findByRole("region", { name: "Agent agency" }); expect(within(invite).getByText("Who do you want on your crew?")).toBeDefined(); fireEvent.click(within(invite).getByRole("button", { name: "Add to crew: Designer" })); @@ -56,7 +56,7 @@ describe("the Bot agency", () => { test("the invitation stays while the page is open, and is not there for a crew that has a team", async () => { const fake = await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Bot agency" }); + const invite = await screen.findByRole("region", { name: "Agent agency" }); fireEvent.click(within(invite).getByRole("button", { name: "Add to crew: Writer" })); await within(invite).findByText("Writer joined the crew"); fireEvent.click(within(invite).getByRole("button", { name: "Add to crew: Designer" })); @@ -75,13 +75,13 @@ describe("the Bot agency", () => { fireEvent.click(within(sidebar()).getAllByRole("button", { name: /^Blog/ })[0] as HTMLElement); await crewOpened("Blog"); await act(async () => {}); - expect(screen.queryByRole("region", { name: "Bot agency" })).toBeNull(); + expect(screen.queryByRole("region", { name: "Agent agency" })).toBeNull(); }); test("the crew's button opens the whole agency, with filters and search", async () => { await openCrewWithChief(); - fireEvent.click(screen.getByRole("button", { name: "Bot agency" })); - const dialog = await screen.findByRole("dialog", { name: "Bot agency" }); + fireEvent.click(screen.getByRole("button", { name: "Agent agency" })); + const dialog = await screen.findByRole("dialog", { name: "Agent agency" }); expect(await within(dialog).findAllByRole("listitem")).toHaveLength(129); fireEvent.click(within(dialog).getByRole("button", { name: "Marketing & sales" })); @@ -96,23 +96,23 @@ describe("the Bot agency", () => { ).toBe("true"); fireEvent.click(within(dialog).getByRole("button", { name: "All" })); - fireEvent.change(within(dialog).getByRole("searchbox", { name: "Search bots" }), { + fireEvent.change(within(dialog).getByRole("searchbox", { name: "Search agents" }), { target: { value: "spreadsheets" }, }); const found = within(dialog).getAllByRole("listitem"); expect(found).toHaveLength(1); expect(within(found[0] as HTMLElement).getByText("Data Analyst")).toBeDefined(); - fireEvent.change(within(dialog).getByRole("searchbox", { name: "Search bots" }), { + fireEvent.change(within(dialog).getByRole("searchbox", { name: "Search agents" }), { target: { value: "zzz" }, }); - expect(within(dialog).getByText("No bot matches that.")).toBeDefined(); + expect(within(dialog).getByText("No agent matches that.")).toBeDefined(); }, 20_000); - test("Learn more shows what the bot does and what it is told, and adds it from there", async () => { + test("Learn more shows what the agent does and what it is told, and adds it from there", async () => { const fake = await openCrewWithChief(); - fireEvent.click(screen.getByRole("button", { name: "Bot agency" })); - const dialog = await screen.findByRole("dialog", { name: "Bot agency" }); + fireEvent.click(screen.getByRole("button", { name: "Agent agency" })); + const dialog = await screen.findByRole("dialog", { name: "Agent agency" }); fireEvent.click( await within(dialog).findByRole("button", { name: "Learn more: Code Reviewer" }), ); @@ -120,7 +120,7 @@ describe("the Bot agency", () => { const detail = await within(dialog).findByRole("region", { name: "Code Reviewer" }); expect(within(detail).getByText("When to call it")).toBeDefined(); expect(within(detail).getByText("Works well with")).toBeDefined(); - const technical = await within(detail).findByText("What the bot is told when it starts"); + const technical = await within(detail).findByText("What the agent is told when it starts"); expect(technical.closest("details")?.textContent).toContain("### What you do"); expect(technical.closest("details")?.textContent).toContain("high"); @@ -128,13 +128,13 @@ describe("the Bot agency", () => { expect(await within(detail).findByText("Code Reviewer joined the crew")).toBeDefined(); expect(addCalls(fake)[0]).toMatchObject({ templateId: "code-reviewer" }); - fireEvent.click(within(dialog).getByRole("button", { name: "Back to all bots" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Back to all agents" })); expect(within(dialog).getAllByRole("listitem")).toHaveLength(129); }); test("the same role twice gets another name", async () => { const fake = await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Bot agency" }); + const invite = await screen.findByRole("region", { name: "Agent agency" }); const add = within(invite).getByRole("button", { name: "Add to crew: Designer" }); fireEvent.click(add); await within(invite).findByText("Designer joined the crew"); @@ -146,9 +146,9 @@ describe("the Bot agency", () => { ]); }); - test("Customize opens the new bot", async () => { + test("Customize opens the new agent", async () => { await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Bot agency" }); + const invite = await screen.findByRole("region", { name: "Agent agency" }); fireEvent.click(within(invite).getByRole("button", { name: "Add to crew: Designer" })); await within(invite).findByText("Designer joined the crew"); fireEvent.click(within(invite).getByRole("button", { name: "Customize" })); @@ -158,7 +158,7 @@ describe("the Bot agency", () => { test("the owner reads it in their language", async () => { setLocaleChoice("pt-BR"); const fake = await openCrewWithChief(); - const invite = await screen.findByRole("region", { name: "Agência de bots" }); + const invite = await screen.findByRole("region", { name: "Agência de agentes" }); expect(within(invite).getByText("Quem você quer na sua equipe?")).toBeDefined(); fireEvent.click(within(invite).getByRole("button", { name: "Adicionar na equipe: Redator" })); expect(await within(invite).findByText("Redator entrou na equipe")).toBeDefined(); diff --git a/app/src/features/chat/agentBot.test.tsx b/app/src/features/chat/agentBot.test.tsx index e278706e..6e6737ef 100644 --- a/app/src/features/chat/agentBot.test.tsx +++ b/app/src/features/chat/agentBot.test.tsx @@ -25,7 +25,7 @@ async function agyBot() { return fake; } -describe("a bot that runs on another agent", () => { +describe("an agent that runs on another agent", () => { test("shows its models, not Claude's, and picks one", async () => { const fake = await agyBot(); expect(screen.queryByRole("button", { name: /^Effort:/ })).toBeNull(); @@ -51,7 +51,7 @@ describe("a bot that runs on another agent", () => { }); describe("what an agent that cannot ask needs from the owner", () => { - test("the commands it may run are a list in the bot's edit dialog", async () => { + test("the commands it may run are a list in the agent's edit dialog", async () => { const fake = await agyBot(); const bot = [...fake.bots.values()][0]; expect(bot?.allowedCommands).toEqual([]); @@ -63,7 +63,7 @@ describe("what an agent that cannot ask needs from the owner", () => { const menu = await screen.findByRole("menu", { name: "Actions for Zed" }); fireEvent.click(within(menu).getByRole("menuitem", { name: "Edit" })); const dialog = within(await screen.findByRole("dialog", { name: /Edit Zed/ })); - fireEvent.change(dialog.getByLabelText("Commands this bot may run"), { + fireEvent.change(dialog.getByLabelText("Commands this agent may run"), { target: { value: "git status\n npm test \ngit status" }, }); fireEvent.click(dialog.getByRole("button", { name: "Save" })); @@ -72,7 +72,7 @@ describe("what an agent that cannot ask needs from the owner", () => { }); describe("Claude Code missing", () => { - test("is only a warning for someone who has a bot that runs on it", async () => { + test("is only a warning for someone who has an agent that runs on it", async () => { const make = async (agent: "claude" | "agy") => { const fake = new FakeBotloft(); fake.system = { ...fake.system, runtimeError: "Claude Code was not found" }; @@ -82,14 +82,14 @@ describe("Claude Code missing", () => { await crewOpened("Ops"); }; await make("agy"); - expect(screen.queryByText("Bots can't start")).toBeNull(); + expect(screen.queryByText("Agents can't start")).toBeNull(); cleanup(); await make("claude"); - expect(await screen.findByText("Bots can't start")).toBeDefined(); + expect(await screen.findByText("Agents can't start")).toBeDefined(); }); }); -describe("a Codex bot", () => { +describe("a Codex agent", () => { test("has an effort and a model list under the message box", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); diff --git a/app/src/features/chat/allowAlways.test.tsx b/app/src/features/chat/allowAlways.test.tsx index a5b5dfa3..4782fd12 100644 --- a/app/src/features/chat/allowAlways.test.tsx +++ b/app/src/features/chat/allowAlways.test.tsx @@ -20,7 +20,7 @@ async function openScout() { const details = () => screen.getByRole("complementary", { name: "About Scout" }); describe("Allow always", () => { - test("allows the request for good and lists it in the bot's details, where it can go", async () => { + test("allows the request for good and lists it in the agent's details, where it can go", async () => { const { fake, scout } = await openScout(); act(() => { fake.chat.ask(scout.id, "Bash", "git status", '{"command":"git status"}'); diff --git a/app/src/features/chat/botChange.test.tsx b/app/src/features/chat/botChange.test.tsx index fbc99884..06cf531e 100644 --- a/app/src/features/chat/botChange.test.tsx +++ b/app/src/features/chat/botChange.test.tsx @@ -31,8 +31,8 @@ const answered = (fake: FakeBotloft) => | { allow: boolean; note?: string } | undefined; -describe("a bot asking to change a bot", () => { - test("the chief's request shows the other bot, before and after, and why", async () => { +describe("an agent asking to change an agent", () => { + test("the chief's request shows the other agent, before and after, and why", async () => { const { fake, chief } = await openChief(); act(() => { fake.chat.ask( @@ -67,7 +67,7 @@ describe("a bot asking to change a bot", () => { await waitFor(() => expect(answered(fake)).toMatchObject({ allow: true })); }); - test("a bot changing itself, declined with a note", async () => { + test("an agent changing itself, declined with a note", async () => { const { fake, chief } = await openChief(); act(() => { fake.chat.ask( diff --git a/app/src/features/chat/chat.test.tsx b/app/src/features/chat/chat.test.tsx index 7d1b9782..0390a935 100644 --- a/app/src/features/chat/chat.test.tsx +++ b/app/src/features/chat/chat.test.tsx @@ -69,7 +69,7 @@ describe("chat", () => { expect(bubble.textContent).toBe("Find three sources\non retrieval"); expect(field().value).toBe(""); const row = bubble.closest("li") as HTMLElement; - expect(within(row).getByText("Waiting for the bot")).toBeDefined(); + expect(within(row).getByText("Waiting for the agent")).toBeDefined(); const sent = fake.conversation.messages.at(-1); act(() => { fake.conversation.read(fake.conversation.deliveryOf(sent?.id ?? "").id); @@ -78,7 +78,7 @@ describe("chat", () => { expect(within(sidebar()).getByText("You: Find three sources on retrieval")).toBeDefined(); }); - test("the reply streams in, then tools and the turn show under the bot", async () => { + test("the reply streams in, then tools and the turn show under the agent", async () => { const { fake, scout } = crew(); await openScout(fake); act(() => { @@ -153,7 +153,7 @@ describe("chat", () => { expect(await within(chat()).findByText("Allowed: write a file")).toBeDefined(); }); - test("a command request leads with the bot's explanation and keeps the command a click away", async () => { + test("a command request leads with the agent's explanation and keeps the command a click away", async () => { const { fake, scout } = crew(); await openScout(fake); const script = "cd C:/Work && python - <<'EOF'\nprint('hello')\nEOF"; @@ -252,7 +252,7 @@ describe("chat", () => { ); }); - test("images sent earlier are read back from the bot's folder", async () => { + test("images sent earlier are read back from the agent's folder", async () => { const { fake, scout } = crew(); await fake.call("messages.send", { botId: scout.id, diff --git a/app/src/features/chat/context.test.tsx b/app/src/features/chat/context.test.tsx index a3407b50..644ecd50 100644 --- a/app/src/features/chat/context.test.tsx +++ b/app/src/features/chat/context.test.tsx @@ -115,7 +115,7 @@ describe("conversation space", () => { expect(within(sidebar()).queryByText(/compacted/)).toBeNull(); }); - test("a busy bot compacts when it finishes what it is doing", async () => { + test("a busy agent compacts when it finishes what it is doing", async () => { const { fake, scout } = await openScout(); act(() => fake.setBotState(scout.id, "busy")); fireEvent.click(within(open()).getByRole("button", { name: "Compact now" })); @@ -124,7 +124,7 @@ describe("conversation space", () => { ).toBeDefined(); }); - test("a bot that is not running cannot compact", async () => { + test("an agent that is not running cannot compact", async () => { const { fake, scout } = await openScout(); act(() => fake.setBotState(scout.id, "offline")); const menu = open(); diff --git a/app/src/features/chat/crewAccess.test.tsx b/app/src/features/chat/crewAccess.test.tsx index 0d528daf..123fb364 100644 --- a/app/src/features/chat/crewAccess.test.tsx +++ b/app/src/features/chat/crewAccess.test.tsx @@ -32,8 +32,8 @@ const request = (bot: string | null) => why: "To get this week's post.", }); -describe("a bot asking to reach another crew", () => { - test("for one bot: only now, always that bot, always the crew or deny", async () => { +describe("an agent asking to reach another crew", () => { + test("for one agent: only now, always that agent, always the crew or deny", async () => { const { fake, scout } = await openScout(); act(() => { fake.chat.ask(scout.id, CREW_ACCESS_TOOL, "Blog", request("Writer")); @@ -51,7 +51,7 @@ describe("a bot asking to reach another crew", () => { expect(JSON.parse(answered(fake)?.input as string)).toEqual({ scope: "bot" }); }); - test("for the whole crew there is no bot to always allow", async () => { + test("for the whole crew there is no agent to always allow", async () => { const { fake, scout } = await openScout(); act(() => { fake.chat.ask(scout.id, CREW_ACCESS_TOOL, "Blog", request(null)); @@ -65,7 +65,7 @@ describe("a bot asking to reach another crew", () => { ); }); - test("lasting access shows in the bot's details and can be taken back", async () => { + test("lasting access shows in the agent's details and can be taken back", async () => { const { fake, scout } = await openScout(); fake.crewAccess.add(scout.id, "Blog"); fake.crewAccess.add(scout.id, "Shop", { id: "bot_clerk", name: "Clerk" }, { edit: true }); diff --git a/app/src/features/chat/effort.test.tsx b/app/src/features/chat/effort.test.tsx index dfbda40d..ca636d1f 100644 --- a/app/src/features/chat/effort.test.tsx +++ b/app/src/features/chat/effort.test.tsx @@ -38,7 +38,7 @@ function slideTo(slider: HTMLElement | null, stop: number) { } describe("effort", () => { - test("a bot runs at its model's own level until the owner moves the slider", async () => { + test("an agent runs at its model's own level until the owner moves the slider", async () => { const { fake, scout } = await openScout(); expect(picker().getAttribute("aria-label")).toBe("Effort: Medium"); const { menu, slider } = open(); @@ -72,7 +72,7 @@ describe("effort", () => { await waitFor(() => expect(efforts(fake)).toEqual([{ botId: scout.id, effort: "default" }])); }); - test("several stops in a row tell the bot only the last", async () => { + test("several stops in a row tell the agent only the last", async () => { const { fake, scout } = await openScout(); const { slider } = open(); slideTo(slider, 2); @@ -90,7 +90,7 @@ describe("effort", () => { await waitFor(() => expect(efforts(fake)).toEqual([{ botId: scout.id, effort: "low" }])); }); - test("a busy bot changes effort when it finishes what it is doing", async () => { + test("a busy agent changes effort when it finishes what it is doing", async () => { const { fake, scout } = await openScout(); act(() => fake.setBotState(scout.id, "busy")); const { slider } = open(); diff --git a/app/src/features/chat/memory.test.tsx b/app/src/features/chat/memory.test.tsx index f2d78cfa..bf48c789 100644 --- a/app/src/features/chat/memory.test.tsx +++ b/app/src/features/chat/memory.test.tsx @@ -31,7 +31,7 @@ function edit(file: string | null, changes: Partial = {}): ChatItem { } describe("whose memory an edit wrote", () => { - test("the bot's own, in any case and with either slash", () => { + test("the agent's own, in any case and with either slash", () => { expect(memoryOf(edit(`${WORKSPACE}\\CLAUDE.md`), places, WORKSPACE)).toBe("bot"); expect(memoryOf(edit("c:/users/owner/botloft/ops/scout/claude.md"), places, WORKSPACE)).toBe( "bot", @@ -48,7 +48,7 @@ describe("whose memory an edit wrote", () => { expect(memoryOf(edit("D:\\Projects\\site\\CLAUDE.md"), places, WORKSPACE)).toBe("crew"); }); - test("nothing for other files, other bots, or calls that did not finish", () => { + test("nothing for other files, other agents, or calls that did not finish", () => { expect(memoryOf(edit(`${WORKSPACE}\\notes.md`), places, WORKSPACE)).toBeNull(); expect( memoryOf(edit("C:\\Users\\owner\\Botloft\\ops\\writer\\CLAUDE.md"), places, WORKSPACE), diff --git a/app/src/features/chat/models.test.tsx b/app/src/features/chat/models.test.tsx index 50bf6038..983a2118 100644 --- a/app/src/features/chat/models.test.tsx +++ b/app/src/features/chat/models.test.tsx @@ -30,7 +30,7 @@ function choose(name: RegExp) { } describe("models", () => { - test("a bot runs on the plan's default until the owner picks a model", async () => { + test("an agent runs on the plan's default until the owner picks a model", async () => { const { fake, scout } = await openScout(); expect(picker().getAttribute("aria-label")).toBe("Model: Default"); fireEvent.click(picker()); @@ -54,7 +54,7 @@ describe("models", () => { expect(screen.getByText("Scout uses your plan's default model, now Opus 5.5")).toBeDefined(); }); - test("a busy bot changes model when it finishes what it is doing", async () => { + test("a busy agent changes model when it finishes what it is doing", async () => { const { fake, scout } = await openScout(); act(() => fake.setBotState(scout.id, "busy")); choose(/Sonnet/); @@ -70,23 +70,23 @@ describe("models", () => { kind: "notice", level: "error", code: "model_unavailable", - text: "Claude Code could not use this bot's model.", + text: "Claude Code could not use this agent's model.", }); }); expect(await screen.findByText(/may not be on your Claude plan/)).toBeDefined(); }); - test("a new bot can start on a chosen model", async () => { + test("a new agent can start on a chosen model", async () => { const fake = new FakeBotloft(); fake.addCrew("Ops"); renderApp(fake); await crewOpened("Ops"); - const [headerButton] = await screen.findAllByRole("button", { name: "New bot" }); + const [headerButton] = await screen.findAllByRole("button", { name: "New agent" }); fireEvent.click(headerButton as HTMLElement); - const dialog = screen.getByRole("dialog", { name: "New bot" }); + const dialog = screen.getByRole("dialog", { name: "New agent" }); fireEvent.change(within(dialog).getByLabelText("Name"), { target: { value: "Writer" } }); fireEvent.change(within(dialog).getByLabelText("Model"), { target: { value: "sonnet" } }); - fireEvent.click(within(dialog).getByRole("button", { name: "Create bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Create agent" })); await screen.findByRole("button", { name: "Model: Sonnet" }); const create = fake.calls.find((call) => call.method === "bots.create"); expect(create?.params).toMatchObject({ name: "Writer", model: "sonnet" }); diff --git a/app/src/features/chat/modes.test.tsx b/app/src/features/chat/modes.test.tsx index 9cfff293..f8a1d23a 100644 --- a/app/src/features/chat/modes.test.tsx +++ b/app/src/features/chat/modes.test.tsx @@ -29,7 +29,7 @@ function choose(name: RegExp) { } describe("permission modes", () => { - test("a bot starts in Manual and the owner picks another mode", async () => { + test("an agent starts in Manual and the owner picks another mode", async () => { const { fake, scout } = await openScout(); expect(picker().getAttribute("aria-label")).toBe("Mode: Manual"); fireEvent.click(picker()); @@ -45,11 +45,11 @@ describe("permission modes", () => { expect(screen.queryByText(/switches to/)).toBeNull(); }); - test("bypassing permissions asks first, then marks the bot in red", async () => { + test("bypassing permissions asks first, then marks the agent in red", async () => { const { fake } = await openScout(); choose(/Bypass permissions/); const dialog = screen.getByRole("dialog", { name: "Let Scout do anything without asking?" }); - expect(within(dialog).getByText(/other bots' files/)).toBeDefined(); + expect(within(dialog).getByText(/other agents' files/)).toBeDefined(); fireEvent.click(within(dialog).getByRole("button", { name: "Cancel" })); expect(modeCalls(fake)).toHaveLength(0); expect(screen.queryByText("Asks nothing")).toBeNull(); @@ -61,7 +61,7 @@ describe("permission modes", () => { expect(modeCalls(fake).at(-1)?.params).toMatchObject({ mode: "bypass_permissions" }); }); - test("a busy bot changes mode when it finishes what it is doing", async () => { + test("a busy agent changes mode when it finishes what it is doing", async () => { const { fake, scout } = await openScout(); act(() => fake.setBotState(scout.id, "busy")); choose(/Plan/); @@ -83,7 +83,7 @@ describe("plans", () => { return screen.getByRole("region", { name: "Scout made a plan and wants to go ahead" }); } - test("the plan shows in full and approving it lets the bot go ahead", async () => { + test("the plan shows in full and approving it lets the agent go ahead", async () => { const { fake, scout } = await openScout(); await act(() => fake.call("bots.setPermissionMode", { botId: scout.id, mode: "plan" })); const card = askToGoAhead(fake, scout.id); diff --git a/app/src/features/chat/readImage.test.tsx b/app/src/features/chat/readImage.test.tsx index beb1ce65..ac846b86 100644 --- a/app/src/features/chat/readImage.test.tsx +++ b/app/src/features/chat/readImage.test.tsx @@ -9,7 +9,7 @@ afterEach(cleanup); const read = (input: string, name = "Read") => ({ name, input }) as ToolItem; -describe("the image a bot reads", () => { +describe("the image an agent reads", () => { test("is found in a Read of a picture, even with its input cut short", () => { expect(readImagePath(read(JSON.stringify({ file_path: "C:\\Work\\q10.png" })))).toBe( "C:\\Work\\q10.png", @@ -51,7 +51,7 @@ describe("the image a bot reads", () => { expect(within(chat).getAllByRole("img", { name: "q10.png" })).toHaveLength(2); }); - test("an image outside the bot's folders keeps the plain line", async () => { + test("an image outside the agent's folders keeps the plain line", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout", "Finds sources"); diff --git a/app/src/features/chat/reply.test.tsx b/app/src/features/chat/reply.test.tsx index f8cb0c5a..43d48571 100644 --- a/app/src/features/chat/reply.test.tsx +++ b/app/src/features/chat/reply.test.tsx @@ -26,7 +26,7 @@ const sent = (fake: FakeBotloft) => fake.calls.filter((call) => call.method === "messages.send").map((call) => call.params); describe("replying to a message", () => { - test("quotes the bot's reply over the composer, sends it, and shows it on the bubble", async () => { + test("quotes the agent's reply over the composer, sends it, and shows it on the bubble", async () => { const { fake, scout, said } = await setup(); fireEvent.click(screen.getByRole("button", { name: "Reply to Scout" })); expect(screen.getByText("Replying to Scout")).toBeDefined(); @@ -43,7 +43,7 @@ describe("replying to a message", () => { expect(quote.title).toBe("Show what this replies to"); }); - test("replies to another bot's message too, and Esc or X cancels", async () => { + test("replies to another agent's message too, and Esc or X cancels", async () => { const { fake } = await setup(); fireEvent.click(screen.getByRole("button", { name: "Reply to Writer" })); const bar = screen.getByText("Replying to Writer").parentElement as HTMLElement; diff --git a/app/src/features/chat/sharedFiles.test.tsx b/app/src/features/chat/sharedFiles.test.tsx index e77e1610..25e99a53 100644 --- a/app/src/features/chat/sharedFiles.test.tsx +++ b/app/src/features/chat/sharedFiles.test.tsx @@ -68,14 +68,19 @@ describe("shared files", () => { test("a file the panel's list leaves out still previews from the card", async () => { const { fake, scout } = crew(); // The list skips files older than the bot; the card still knows it. - const old = fake.files.add(scout.id, "old.md", { text: "# Older than the bot", listed: false }); + const old = fake.files.add(scout.id, "old.md", { + text: "# Older than the agent", + listed: false, + }); share(fake, scout.id, [old]); await openScout(fake); const list = await cards(); fireEvent.click(within(list).getByRole("button", { name: /^Preview/ })); const panel = await screen.findByRole("complementary", { name: "Files from Scout" }); - expect(await within(panel).findByRole("heading", { name: "Older than the bot" })).toBeDefined(); + expect( + await within(panel).findByRole("heading", { name: "Older than the agent" }), + ).toBeDefined(); }); test("a share that failed stays a tool line with its error", async () => { diff --git a/app/src/features/chat/toolGroup.test.tsx b/app/src/features/chat/toolGroup.test.tsx index 81b767d0..b7efe233 100644 --- a/app/src/features/chat/toolGroup.test.tsx +++ b/app/src/features/chat/toolGroup.test.tsx @@ -31,7 +31,7 @@ describe("a group of tool calls", () => { expect(toolSummary([done("mcp__other__frobnicate")], en.chat.tools.group)).toBe("Used 1 tool"); }); - test("says what the bot does now, then folds into its summary", async () => { + test("says what the agent does now, then folds into its summary", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout", "Finds sources"); diff --git a/app/src/features/connections/connections.test.tsx b/app/src/features/connections/connections.test.tsx index 6f4eecac..ef113307 100644 --- a/app/src/features/connections/connections.test.tsx +++ b/app/src/features/connections/connections.test.tsx @@ -50,7 +50,7 @@ describe("connected tools", () => { expect(screen.queryByRole("dialog", { name: "Connect a tool" })).toBeNull(), ); expect(within(dialog).getByText("LinkedIn".toLowerCase())).toBeDefined(); - expect(within(dialog).getByText("No bot uses it yet")).toBeDefined(); + expect(within(dialog).getByText("No agent uses it yet")).toBeDefined(); const saved = fake.calls.find((each) => each.method === "mcp.save"); expect(saved?.params).toMatchObject({ name: "linkedin", @@ -95,7 +95,7 @@ describe("connected tools", () => { ); }); - test("a bot gets a tool when the owner turns it on, and shows how it came up", async () => { + test("an agent gets a tool when the owner turns it on, and shows how it came up", async () => { const { fake, scout, dialog } = await openTools(); fireEvent.click(await within(dialog).findByRole("button", { name: "Connect a tool…" })); const adding = screen.getByRole("dialog", { name: "Connect a tool" }); diff --git a/app/src/features/crews/botMenu.test.tsx b/app/src/features/crews/botMenu.test.tsx index 3512abdb..136be78f 100644 --- a/app/src/features/crews/botMenu.test.tsx +++ b/app/src/features/crews/botMenu.test.tsx @@ -29,8 +29,8 @@ const labels = (menu: HTMLElement) => .getAllByRole("menuitem") .map((item) => item.textContent); -describe("a bot's right-click menu", () => { - test("offers what the header's menu offers, without opening the bot", async () => { +describe("an agent's right-click menu", () => { + test("offers what the header's menu offers, without opening the agent", async () => { await twoBots(); const menu = rightClick("Scout"); const offered = labels(menu); @@ -40,8 +40,8 @@ describe("a bot's right-click menu", () => { "Restart with a new conversation", "Open folder", "Mark as unread", - "Archive bot", - "Delete bot", + "Archive agent", + "Delete agent", ]); // The crew's page is still the one open. expect(screen.getByRole("heading", { level: 1, name: "Ops" })).toBeDefined(); @@ -51,11 +51,11 @@ describe("a bot's right-click menu", () => { fireEvent.keyDown(window, { key: "Escape" }); expect(screen.queryByRole("menu")).toBeNull(); openBot("Scout"); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); expect(labels(screen.getByRole("menu"))).toEqual(offered); }); - test("acts on the bot that was clicked, not the one that is open", async () => { + test("acts on the agent that was clicked, not the one that is open", async () => { const { fake, host, crew, scout, writer } = await twoBots(); openBot("Writer"); await screen.findByRole("heading", { level: 1, name: "Writer" }); @@ -69,10 +69,10 @@ describe("a bot's right-click menu", () => { expect(labels(rightClick("Scout"))).toContain("Stop being chief"); expect(labels(rightClick("Writer"))).toContain("Make crew chief"); - fireEvent.click(within(rightClick("Scout")).getByRole("menuitem", { name: "Archive bot" })); + fireEvent.click(within(rightClick("Scout")).getByRole("menuitem", { name: "Archive agent" })); const dialog = screen.getByRole("dialog", { name: "Archive Scout?" }); expect(sidebar().contains(dialog)).toBe(false); - fireEvent.click(within(dialog).getByRole("button", { name: "Archive bot" })); + fireEvent.click(within(dialog).getByRole("button", { name: "Archive agent" })); await waitFor(() => expect(within(sidebar()).queryByRole("button", { name: /Scout/ })).toBeNull(), ); @@ -85,7 +85,7 @@ describe("a bot's right-click menu", () => { expect(fake.bots.get(writer.id)?.archivedAt).toBeNull(); }); - test("edits from the menu and keeps a paused bot from restarting", async () => { + test("edits from the menu and keeps a paused agent from restarting", async () => { const { fake, scout } = await twoBots(); await fake.call("bots.setPaused", { botId: scout.id, paused: true }); const menu = rightClick("Scout"); @@ -117,7 +117,7 @@ describe("a bot's right-click menu", () => { expect(document.activeElement).toBe(scout); }); - test("closes on a click elsewhere, and another bot's menu takes its place", async () => { + test("closes on a click elsewhere, and another agent's menu takes its place", async () => { await twoBots(); rightClick("Scout"); fireEvent.pointerDown(row("Writer")); diff --git a/app/src/features/crews/chief.test.tsx b/app/src/features/crews/chief.test.tsx index c860ccf2..9f919fcf 100644 --- a/app/src/features/crews/chief.test.tsx +++ b/app/src/features/crews/chief.test.tsx @@ -35,7 +35,7 @@ function suggest(fake: FakeBotloft, chiefId: string) { act(() => { fake.chat.ask(chiefId, SUGGEST_TOOL, "Designer", JSON.stringify(DESIGNER)); }); - return screen.getByRole("region", { name: "Chief suggests a new bot" }); + return screen.getByRole("region", { name: "Chief suggests a new agent" }); } const answer = (fake: FakeBotloft) => @@ -67,12 +67,12 @@ describe("the crew's chief", () => { expect(screen.getAllByTitle(/^Leads Bakery/)).toHaveLength(2); }); - test("the owner creates the bot the chief suggests", async () => { + test("the owner creates the agent the chief suggests", async () => { const { fake, chiefId } = await openChief(); const card = suggest(fake, chiefId); expect(within(card).getByText(DESIGNER.reason)).toBeDefined(); expect((within(card).getByLabelText("Name") as HTMLInputElement).value).toBe("Designer"); - fireEvent.click(within(card).getByRole("button", { name: "Create bot" })); + fireEvent.click(within(card).getByRole("button", { name: "Create agent" })); expect(await screen.findByText("You created Designer")).toBeDefined(); expect(answer(fake)).toEqual({ approvalId: expect.any(String), allow: true }); @@ -84,7 +84,7 @@ describe("the crew's chief", () => { const card = suggest(fake, chiefId); fireEvent.change(within(card).getByLabelText("Name"), { target: { value: "Illustrator" } }); fireEvent.change(within(card).getByLabelText("Model"), { target: { value: "haiku" } }); - fireEvent.click(within(card).getByRole("button", { name: "Create bot" })); + fireEvent.click(within(card).getByRole("button", { name: "Create agent" })); expect(await screen.findByText("You created Illustrator")).toBeDefined(); const sent = answer(fake) as { input: string }; @@ -106,21 +106,21 @@ describe("the crew's chief", () => { expect([...fake.bots.values()].some((bot) => bot.name === "Designer")).toBe(false); }); - test("the owner moves the chief to another bot", async () => { + test("the owner moves the chief to another agent", async () => { const { fake, crew } = await openChief(); act(() => { fake.setBotState(fake.addBot(crew.id, "Writer").id, "idle"); }); openBot("Writer"); await screen.findByRole("heading", { level: 1, name: "Writer" }); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); fireEvent.click(screen.getByRole("menuitem", { name: "Make crew chief" })); // The header and the conversation list of the new chief. await screen.findByText("Chief", { selector: "header span" }); const moved = fake.calls.find((call) => call.method === "crews.setLead"); expect(moved?.params).toMatchObject({ crewId: crew.id }); - fireEvent.click(screen.getByRole("button", { name: "More bot actions" })); + fireEvent.click(screen.getByRole("button", { name: "More agent actions" })); expect(screen.getByRole("menuitem", { name: "Stop being chief" })).toBeDefined(); }); }); diff --git a/app/src/features/crews/collapse.test.tsx b/app/src/features/crews/collapse.test.tsx index f6c44560..c2eb9d6c 100644 --- a/app/src/features/crews/collapse.test.tsx +++ b/app/src/features/crews/collapse.test.tsx @@ -26,7 +26,7 @@ function twoCrews() { } describe("folding a crew in the sidebar", () => { - test("hides its bots, keeps the open one, and is remembered", async () => { + test("hides its agents, keeps the open one, and is remembered", async () => { const { fake, ops, scout } = twoCrews(); renderApp(fake); await crewOpened("Ops"); @@ -42,7 +42,7 @@ describe("folding a crew in the sidebar", () => { // A bot waiting for the owner shows on the folded crew. act(() => fake.setBotState(scout.id, "needs_approval")); expect(within(sidebar()).getByRole("button", { name: /^Ops/ }).textContent).toContain( - "a bot needs you", + "an agent needs you", ); const unfold = within(sidebar()).getByRole("button", { name: "Unfold Ops" }); @@ -76,7 +76,7 @@ describe("the page of all crews", () => { fireEvent.click(within(sidebar()).getByRole("button", { name: "Crews" })); await crewOpened("Crews"); const page = screen.getByRole("region", { name: "Crews" }); - expect(page.textContent).toContain("2 crews · 3 bots"); + expect(page.textContent).toContain("2 crews · 3 agents"); const docs = within(page).getByRole("button", { name: /^Docs/ }); expect(docs.textContent).toContain("1 working"); expect(within(page).getByRole("button", { name: /^Ops/ }).textContent).toContain("All quiet"); diff --git a/app/src/features/crews/crewMenu.test.tsx b/app/src/features/crews/crewMenu.test.tsx index 7f13a93b..2775678c 100644 --- a/app/src/features/crews/crewMenu.test.tsx +++ b/app/src/features/crews/crewMenu.test.tsx @@ -37,7 +37,7 @@ describe("a crew's right-click menu", () => { .getAllByRole("menuitem") .map((one) => one.textContent), ).toEqual([ - "New bot", + "New agent", "Pause crew", "Move up", "Rename", diff --git a/app/src/features/crews/crewTemplates.test.tsx b/app/src/features/crews/crewTemplates.test.tsx index 11a4fec8..350b46c4 100644 --- a/app/src/features/crews/crewTemplates.test.tsx +++ b/app/src/features/crews/crewTemplates.test.tsx @@ -39,7 +39,7 @@ describe("the crew templates", () => { expect(within(chooser).getByRole("button", { name: /Start with an empty crew/ })).toBeDefined(); }); - test("picking a template fills the form and says which bots it adds", async () => { + test("picking a template fills the form and says which agents it adds", async () => { const { chooser } = await openTemplates(); fireEvent.click(within(chooser).getByRole("button", { name: /Game studio/ })); @@ -56,7 +56,7 @@ describe("the crew templates", () => { expect(screen.getByRole("dialog", { name: "Start from a template" })).toBeDefined(); }); - test("creating from a template makes the crew with its chief and then each bot", async () => { + test("creating from a template makes the crew with its chief and then each agent", async () => { const { fake, chooser } = await openTemplates(); fireEvent.click(within(chooser).getByRole("button", { name: /Online store/ })); const dialog = screen.getByRole("dialog", { name: "New crew" }); @@ -77,7 +77,7 @@ describe("the crew templates", () => { expect(await within(sidebar()).findByRole("button", { name: /^Bookkeeper/ })).toBeDefined(); }); - test("a bot that cannot be added is told, and the crew stays", async () => { + test("an agent that cannot be added is told, and the crew stays", async () => { const { fake, chooser } = await openTemplates(); fireEvent.click(within(chooser).getByRole("button", { name: /Research desk/ })); fake.failNext("catalog.add", new RpcError(-32603, "no room")); @@ -87,7 +87,7 @@ describe("the crew templates", () => { }), ); - expect(await screen.findByText(/1 bot could not be added/)).toBeDefined(); + expect(await screen.findByText(/1 agent could not be added/)).toBeDefined(); expect(fake.calls.filter((call) => call.method === "catalog.add")).toHaveLength(6); expect(await screen.findByRole("heading", { level: 1, name: "Chief" })).toBeDefined(); }); diff --git a/app/src/features/crews/preview.test.tsx b/app/src/features/crews/preview.test.tsx index bac54be9..a81ef87e 100644 --- a/app/src/features/crews/preview.test.tsx +++ b/app/src/features/crews/preview.test.tsx @@ -5,7 +5,7 @@ import { crewOpened, renderApp, sidebar } from "../../test/app"; afterEach(cleanup); -test("a bot's last reply shows in the list as plain words, a command as it is", async () => { +test("an agent's last reply shows in the list as plain words, a command as it is", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Bakery"); const sales = fake.addBot(crew.id, "Sales"); diff --git a/app/src/features/crews/stateFilter.test.tsx b/app/src/features/crews/stateFilter.test.tsx index 9bd2720a..5e27bd74 100644 --- a/app/src/features/crews/stateFilter.test.tsx +++ b/app/src/features/crews/stateFilter.test.tsx @@ -10,7 +10,7 @@ afterEach(() => { }); describe("state filters in the conversation list", () => { - test("a chip narrows the list to the bots in that state, with counts", async () => { + test("a chip narrows the list to the agents in that state, with counts", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const calm = fake.addCrew("Calm"); diff --git a/app/src/features/crews/unread.test.tsx b/app/src/features/crews/unread.test.tsx index 46d282ad..4731f7da 100644 --- a/app/src/features/crews/unread.test.tsx +++ b/app/src/features/crews/unread.test.tsx @@ -22,7 +22,7 @@ const row = (name: string) => within(sidebar()).getByRole("button", { name: new RegExp(`^${name},`) }); describe("unread replies in the conversation list", () => { - test("a bot that replied stays marked until the owner opens its chat", async () => { + test("an agent that replied stays marked until the owner opens its chat", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); @@ -77,7 +77,7 @@ describe("unread replies in the conversation list", () => { expect(row("Scout").getAttribute("aria-label")).toBe("Scout, Idle"); }); - test("the owner marks a bot unread from its menu, and read again", async () => { + test("the owner marks an agent unread from its menu, and read again", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); @@ -107,7 +107,7 @@ describe("unread replies in the conversation list", () => { expect(row("Scout").getAttribute("aria-label")).toBe("Scout, Idle"); }); - test("an unread bot marks the app's icon, unless the owner turned that off", async () => { + test("an unread agent marks the app's icon, unless the owner turned that off", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); diff --git a/app/src/features/desktop/away.test.tsx b/app/src/features/desktop/away.test.tsx index 8cd6878e..dd0e5098 100644 --- a/app/src/features/desktop/away.test.tsx +++ b/app/src/features/desktop/away.test.tsx @@ -65,7 +65,7 @@ describe("use while the owner is away", () => { await waitFor(() => expect(fake.desktop.grants[0]?.unattended).toBe(false)); }, 15_000); - test("the owner hears, when back, what each bot used and opens its chat", async () => { + test("the owner hears, when back, what each agent used and opens its chat", async () => { const { fake, scout } = await opened(); act(() => { fake.desktop.usedAway(scout.id, "Notepad"); diff --git a/app/src/features/desktop/desktop.test.tsx b/app/src/features/desktop/desktop.test.tsx index 7f1eea92..f1424967 100644 --- a/app/src/features/desktop/desktop.test.tsx +++ b/app/src/features/desktop/desktop.test.tsx @@ -72,7 +72,7 @@ describe("the desktop", () => { expect(await screen.findByText("Scout may not use Notepad")).toBeDefined(); }); - test("the bot's details list what it may see on the desktop, and it can be taken back", async () => { + test("the agent's details list what it may see on the desktop, and it can be taken back", async () => { const { fake, scout } = await openScout(); fireEvent.click(screen.getByRole("button", { name: /^Show details/ })); expect( diff --git a/app/src/features/desktop/panel.test.tsx b/app/src/features/desktop/panel.test.tsx index 7f5b7c56..0497a525 100644 --- a/app/src/features/desktop/panel.test.tsx +++ b/app/src/features/desktop/panel.test.tsx @@ -25,7 +25,7 @@ const panel = () => screen.getByRole("complementary", { name: "Scout's desktop" const NOTES = { id: 42, title: "Shopping list - Notepad", app: "Notepad" }; describe("the desktop panel", () => { - test("opens from a desktop tool in the chat and shows the window the bot uses, live", async () => { + test("opens from a desktop tool in the chat and shows the window the agent uses, live", async () => { const { fake, scout } = await openScout(); fireEvent.click( screen.getByRole("button", { name: "Watch on the desktop panel: Read a window" }), @@ -65,7 +65,7 @@ describe("the desktop panel", () => { await waitFor(() => expect(fake.desktop.watching).toBeNull()); }); - test("stops the bot on the desktop and lets it go on", async () => { + test("stops the agent on the desktop and lets it go on", async () => { const { fake, scout } = await openScout(); fireEvent.click( screen.getByRole("button", { name: "Watch on the desktop panel: Read a window" }), @@ -82,7 +82,7 @@ describe("the desktop panel", () => { expect(within(panel()).queryByText("You stopped Scout on your desktop")).toBeNull(); }); - test("opens by itself when the bot starts using the desktop, once per run", async () => { + test("opens by itself when the agent starts using the desktop, once per run", async () => { const { fake, scout } = await openScout(); expect(screen.queryByRole("complementary", { name: "Scout's desktop" })).toBeNull(); act(() => { diff --git a/app/src/features/desktop/realInput.test.tsx b/app/src/features/desktop/realInput.test.tsx index d74343dd..498769d4 100644 --- a/app/src/features/desktop/realInput.test.tsx +++ b/app/src/features/desktop/realInput.test.tsx @@ -56,7 +56,7 @@ describe("the real mouse and keyboard", () => { await waitFor(() => expect(fake.desktop.grants[0]?.realInput).toBe(false)); }, 15_000); - test("are not offered where the bot may only see", async () => { + test("are not offered where the agent may only see", async () => { const { fake, scout } = await openScout(); act(() => { fake.desktop.grant(scout.id, NOTEPAD, "Notepad"); diff --git a/app/src/features/files/files.test.tsx b/app/src/features/files/files.test.tsx index 02d99f06..a3f92566 100644 --- a/app/src/features/files/files.test.tsx +++ b/app/src/features/files/files.test.tsx @@ -26,7 +26,7 @@ const toggle = () => screen.getByRole("button", { name: /Show files|Hide files/ const panel = () => screen.getByRole("complementary", { name: "Files from Scout" }); describe("files panel", () => { - test("shows what the bot made, newest first, and previews text", async () => { + test("shows what the agent made, newest first, and previews text", async () => { const { fake, scout } = crew(); const now = fake.now; fake.files.add(scout.id, "sources.md", { text: "# Sources\n\n- one", at: now - 60_000 }); @@ -70,7 +70,7 @@ describe("files panel", () => { expect(fake.calls.some((call) => call.method === "files.read")).toBe(false); }); - test("an empty bot says it has made nothing yet", async () => { + test("an empty agent says it has made nothing yet", async () => { const { fake } = crew(); await openScout(fake); fireEvent.click(toggle()); diff --git a/app/src/features/messages/calls.test.tsx b/app/src/features/messages/calls.test.tsx index c264868e..b4dda597 100644 --- a/app/src/features/messages/calls.test.tsx +++ b/app/src/features/messages/calls.test.tsx @@ -46,7 +46,7 @@ function delivery(messageId: string, changes: Partial = {}): Delivery } describe("calls", () => { - test("only a bot's message to another bot is a call", () => { + test("only an agent's message to another agent is a call", () => { const empty = { calls: {}, deliveries: {} }; expect(withCall(empty, message("m1", { fromKind: "owner", fromBotId: null }))).toBeNull(); expect(withCall(empty, message("m1", { toBotId: "bot_a" }))).toBeNull(); @@ -66,7 +66,7 @@ describe("calls", () => { expect(at({ m1: delivery("m1", { state: "dead" }) }, NOW + 1000)).toEqual([]); }); - test("one pill per pair of bots, the latest call", () => { + test("one pill per pair of agents, the latest call", () => { let state = { calls: {}, deliveries: {} }; state = { ...state, ...withCall(state, message("m1"), NOW) }; state = { ...state, ...withCall(state, message("m2", { createdAt: NOW + 5 }), NOW) }; @@ -94,14 +94,14 @@ describe("in the app", () => { act(() => { sent = fake.conversation.say({ from: scout.id, to: writer.id, body: "Draft the report" }); }); - const pills = () => screen.getByRole("list", { name: "Bots calling each other" }); + const pills = () => screen.getByRole("list", { name: "Agents calling each other" }); expect(within(pills()).getByText("Calling Writer")).toBeDefined(); expect(within(pills()).getByText("Scout:")).toBeDefined(); // The crew page shows it too. fireEvent.click(screen.getAllByRole("button", { name: "Ops" })[0] as HTMLElement); expect( - within(await screen.findByRole("list", { name: "Bots calling each other" })).getByText( + within(await screen.findByRole("list", { name: "Agents calling each other" })).getByText( "Calling Writer", ), ).toBeDefined(); diff --git a/app/src/features/messages/messages.test.tsx b/app/src/features/messages/messages.test.tsx index 82eb62a4..48a02f61 100644 --- a/app/src/features/messages/messages.test.tsx +++ b/app/src/features/messages/messages.test.tsx @@ -18,7 +18,7 @@ function crew() { const messages = () => screen.getByRole("list", { name: "Messages" }); describe("messages", () => { - test("the crew timeline shows what bots send and what the owner writes", async () => { + test("the crew timeline shows what agents send and what the owner writes", async () => { const { fake, lead, writer } = crew(); const { delivery } = fake.conversation.say({ from: lead.id, @@ -39,7 +39,7 @@ describe("messages", () => { const sent = await within(messages()).findByText("Ship it today"); const sentRow = sent.closest("li") as HTMLElement; expect(within(sentRow).getByText("You")).toBeDefined(); - expect(within(sentRow).getByText("Waiting for the bot")).toBeDefined(); + expect(within(sentRow).getByText("Waiting for the agent")).toBeDefined(); expect(fake.calls.at(-1)).toEqual({ method: "messages.send", params: { botId: writer.id, body: "Ship it today" }, @@ -51,7 +51,7 @@ describe("messages", () => { expect(within(sentRow).getByText("Delivered")).toBeDefined(); }); - test("a message the bot has read gets its two marks in blue, with the word beside them", async () => { + test("a message the agent has read gets its two marks in blue, with the word beside them", async () => { const { fake, lead, writer } = crew(); const { delivery } = fake.conversation.say({ from: lead.id, @@ -101,12 +101,12 @@ describe("messages", () => { test("a message that was not delivered can be retried from the title bar", async () => { const { fake, lead, writer } = crew(); const { delivery } = fake.conversation.say({ from: lead.id, to: writer.id, body: "lost note" }); - fake.conversation.deliver(delivery.id, "dead", "the bot's inbox did not answer"); + fake.conversation.deliver(delivery.id, "dead", "the agent's inbox did not answer"); const { host } = renderApp(fake); fireEvent.click(await screen.findByRole("button", { name: "1 not delivered" })); const dialog = screen.getByRole("dialog", { name: "Messages not delivered" }); expect(await within(dialog).findByText("lost note")).toBeDefined(); - expect(within(dialog).getByText("the bot's inbox did not answer")).toBeDefined(); + expect(within(dialog).getByText("the agent's inbox did not answer")).toBeDefined(); expect(host.attention).toBe(true); fireEvent.click(within(dialog).getByRole("button", { name: "Retry all" })); await vi.waitFor(() => @@ -126,10 +126,10 @@ describe("messages", () => { const row = (await within(messages()).findByText("stuck")).closest("li") as HTMLElement; expect(within(row).getByText("Not delivered")).toBeDefined(); fireEvent.click(within(row).getByRole("button", { name: "Retry" })); - expect(await within(row).findByText("Waiting for the bot")).toBeDefined(); + expect(await within(row).findByText("Waiting for the agent")).toBeDefined(); }); - test("a bot waiting for approval marks the taskbar icon", async () => { + test("an agent waiting for approval marks the taskbar icon", async () => { const { fake, lead } = crew(); const { host } = renderApp(fake); await crewOpened("Ops"); diff --git a/app/src/features/onboarding/agents.test.tsx b/app/src/features/onboarding/agents.test.tsx index eec46e18..4dea0daa 100644 --- a/app/src/features/onboarding/agents.test.tsx +++ b/app/src/features/onboarding/agents.test.tsx @@ -18,7 +18,7 @@ describe("the first screen for someone who does not use Claude Code", () => { renderApp(fake); expect(await screen.findByText("Antigravity (experimental)")).toBeDefined(); expect(screen.getByText("Version 1.3.1")).toBeDefined(); - expect(screen.getByText(/only need it for bots that run on Claude Code/)).toBeDefined(); + expect(screen.getByText(/only need it for agents that run on Claude Code/)).toBeDefined(); expect(screen.queryByText(/Install Claude Code/i)).toBeNull(); }); @@ -34,7 +34,7 @@ describe("the first screen for someone who does not use Claude Code", () => { }); }); -describe("the agent for new bots", () => { +describe("the agent for new agents", () => { test("the new-bot dialog starts on the owner's choice", async () => { const fake = new FakeBotloft(); fake.system = { ...fake.system, enabledAgents: ["claude", "agy"] }; @@ -42,10 +42,10 @@ describe("the agent for new bots", () => { fake.addCrew("Ops"); renderApp(fake); await crewOpened("Ops"); - const [button] = await screen.findAllByRole("button", { name: "New bot" }); + const [button] = await screen.findAllByRole("button", { name: "New agent" }); fireEvent.click(button as HTMLElement); - const dialog = within(await screen.findByRole("dialog", { name: "New bot" })); - expect((dialog.getByLabelText("Agent") as HTMLSelectElement).value).toBe("agy"); + const dialog = within(await screen.findByRole("dialog", { name: "New agent" })); + expect((dialog.getByLabelText("Runs on") as HTMLSelectElement).value).toBe("agy"); }); test("full access is a switch over the command list", async () => { @@ -68,7 +68,7 @@ describe("the agent for new bots", () => { ); const dialog = within(await screen.findByRole("dialog", { name: /Edit Zed/ })); fireEvent.click(dialog.getByRole("switch", { name: "Run any command without asking" })); - expect(dialog.queryByLabelText("Commands this bot may run")).toBeNull(); + expect(dialog.queryByLabelText("Commands this agent may run")).toBeNull(); fireEvent.click(dialog.getByRole("button", { name: "Save" })); await waitFor(() => expect(fake.bot(bot.id).allowedCommands).toEqual(["*"])); }); diff --git a/app/src/features/onboarding/signIn.test.tsx b/app/src/features/onboarding/signIn.test.tsx index 7200746c..aff2af4d 100644 --- a/app/src/features/onboarding/signIn.test.tsx +++ b/app/src/features/onboarding/signIn.test.tsx @@ -59,7 +59,7 @@ describe("signing in to Claude", () => { ).toBeDefined(); }); - test("a bot stopped by a sign-in error offers the sign-in", async () => { + test("an agent stopped by a sign-in error offers the sign-in", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const bot = fake.addBot(crew.id, "Scout"); diff --git a/app/src/features/questions/questions.test.tsx b/app/src/features/questions/questions.test.tsx index f3688786..0555f674 100644 --- a/app/src/features/questions/questions.test.tsx +++ b/app/src/features/questions/questions.test.tsx @@ -67,7 +67,7 @@ describe("questions to the owner", () => { fireEvent.click(entry()); const box = await screen.findByRole("region", { name: "Questions" }); expect( - screen.getByRole("heading", { level: 1, name: "Questions from your bots" }), + screen.getByRole("heading", { level: 1, name: "Questions from your agents" }), ).toBeDefined(); const cards = within(box).getAllByRole("region", { name: /asks$/ }); expect(cards.map((card) => card.getAttribute("aria-label"))).toEqual([ @@ -91,7 +91,7 @@ describe("questions to the owner", () => { await waitFor(() => expect(host.attention).toBe(false)); }); - test("a question in the box leads to its bot's chat", async () => { + test("a question in the box leads to its agent's chat", async () => { const { fake, scout } = crew(); renderApp(fake); await crewOpened("Ops"); diff --git a/app/src/features/routines/routineChange.test.tsx b/app/src/features/routines/routineChange.test.tsx index 5c7e9b47..20fb9f61 100644 --- a/app/src/features/routines/routineChange.test.tsx +++ b/app/src/features/routines/routineChange.test.tsx @@ -34,7 +34,7 @@ const answered = (fake: FakeBotloft) => | { allow: boolean; input?: string; note?: string } | undefined; -describe("a bot changing or deleting its routine", () => { +describe("an agent changing or deleting its routine", () => { test("the change shows before and after, and goes as asked", async () => { const { fake, mail } = await openMail(); const after = { diff --git a/app/src/features/routines/routineRequest.test.tsx b/app/src/features/routines/routineRequest.test.tsx index 8b6906f1..8b7ed5ff 100644 --- a/app/src/features/routines/routineRequest.test.tsx +++ b/app/src/features/routines/routineRequest.test.tsx @@ -47,7 +47,7 @@ const answer = (fake: FakeBotloft) => const routines = (fake: FakeBotloft) => [...fake.routines.routines.values()]; -describe("a bot asking for a routine", () => { +describe("an agent asking for a routine", () => { test("the owner creates it as asked", async () => { const { fake, chiefId } = await openChief(); const card = ask(fake, chiefId, INBOX, "Chief wants to set up a routine"); @@ -79,7 +79,7 @@ describe("a bot asking for a routine", () => { expect(routines(fake)[0]?.schedule).toEqual(sent.schedule); }); - test("a routine for another bot of the crew goes to that bot", async () => { + test("a routine for another agent of the crew goes to that agent", async () => { const { fake, chiefId, mail } = await openChief(); const card = ask( fake, diff --git a/app/src/features/routines/routines.test.tsx b/app/src/features/routines/routines.test.tsx index 75790d8f..aefa0aa1 100644 --- a/app/src/features/routines/routines.test.tsx +++ b/app/src/features/routines/routines.test.tsx @@ -64,7 +64,7 @@ describe("the schedule in words", () => { }); describe("routines", () => { - test("a bot with none says what a routine is", async () => { + test("an agent with none says what a routine is", async () => { await openRoutines(); expect(screen.getByText("Scout has no routines yet.")).toBeDefined(); }); @@ -185,7 +185,7 @@ describe("routines", () => { expect(within(chat).getByText(prompt)).toBeDefined(); }); - test("the crew's page lists every bot's routines", async () => { + test("the crew's page lists every agent's routines", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout"); @@ -202,7 +202,7 @@ describe("routines", () => { }); describe("failed runs", () => { - test("mark the taskbar until the owner opens the bot", async () => { + test("mark the taskbar until the owner opens the agent", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const scout = fake.addBot(ops.id, "Scout"); diff --git a/app/src/features/routines/routinesPage.test.tsx b/app/src/features/routines/routinesPage.test.tsx index 8e766d7c..a53f8ee1 100644 --- a/app/src/features/routines/routinesPage.test.tsx +++ b/app/src/features/routines/routinesPage.test.tsx @@ -68,7 +68,7 @@ describe("the routines page", () => { expect(screen.getByRole("dialog", { name: "Edit Morning summary" })).toBeDefined(); }); - test("groups each bot's routines under its mascot, folded on demand", async () => { + test("groups each agent's routines under its mascot, folded on demand", async () => { await openPage(setup()); const watcher = screen.getByRole("region", { name: "Watcher" }); const list = within(watcher).getByRole("list", { name: "Watcher's routines" }); diff --git a/app/src/features/routines/signals.test.tsx b/app/src/features/routines/signals.test.tsx index 0c19cb8f..c3aed4f8 100644 --- a/app/src/features/routines/signals.test.tsx +++ b/app/src/features/routines/signals.test.tsx @@ -25,11 +25,11 @@ async function openRoutines() { describe("routines that wait for a signal", () => { test("the schedule reads as the signal", () => { expect(describeSchedule({ kind: "signal", name: "report-ready" }, en.routines.when)).toBe( - "When a bot signals “report-ready”", + "When an agent signals “report-ready”", ); }); - test("the owner makes one, and a bot's signal runs it", async () => { + test("the owner makes one, and an agent's signal runs it", async () => { const { fake, writer } = await openRoutines(); fireEvent.click(screen.getByRole("button", { name: "New routine" })); const dialog = screen.getByRole("dialog", { name: "New routine for Scout" }); @@ -37,9 +37,9 @@ describe("routines that wait for a signal", () => { fireEvent.change(within(dialog).getByLabelText("What should Scout do?"), { target: { value: "Review the report" }, }); - fireEvent.click(within(dialog).getByRole("radio", { name: "When a bot signals" })); + fireEvent.click(within(dialog).getByRole("radio", { name: "When an agent signals" })); expect(within(dialog).queryByLabelText("At")).toBeNull(); - expect(within(dialog).getByText(/Scout runs this when a bot of the crew/)).toBeDefined(); + expect(within(dialog).getByText(/Scout runs this when an agent of the crew/)).toBeDefined(); fireEvent.change(within(dialog).getByLabelText("Signal"), { target: { value: "Report Ready" }, }); @@ -52,7 +52,7 @@ describe("routines that wait for a signal", () => { await screen.findByText("Review"); const params = fake.calls.find((call) => call.method === "routines.create")?.params; expect(params).toMatchObject({ schedule: { kind: "signal", name: "Report Ready" } }); - expect(screen.getByText(/When a bot signals “report-ready”/)).toBeDefined(); + expect(screen.getByText(/When an agent signals “report-ready”/)).toBeDefined(); expect(screen.getByText(/Waits for a signal/)).toBeDefined(); act(() => { @@ -76,7 +76,7 @@ describe("routines that wait for a signal", () => { fireEvent.click(await screen.findByRole("button", { name: "More actions for Review" })); fireEvent.click(screen.getByRole("menuitem", { name: "Edit" })); const dialog = screen.getByRole("dialog", { name: "Edit Review" }); - expect(within(dialog).getByRole("radio", { name: "When a bot signals" })).toHaveProperty( + expect(within(dialog).getByRole("radio", { name: "When an agent signals" })).toHaveProperty( "checked", true, ); diff --git a/app/src/features/screens/cursor.test.tsx b/app/src/features/screens/cursor.test.tsx index 807649a7..c2d13ef4 100644 --- a/app/src/features/screens/cursor.test.tsx +++ b/app/src/features/screens/cursor.test.tsx @@ -29,7 +29,7 @@ function say(data: unknown, source: Window | null) { }); } -describe("the bot's cursor on a screen", () => { +describe("the agent's cursor on a screen", () => { test("follows what the draft's script says, scaled to the artboard", () => { const { view, frame } = draw(); expect(view.container.querySelector(".bot-cursor")).toBeNull(); @@ -75,7 +75,7 @@ describe("the bot's cursor on a screen", () => { expect(cursor.style.left).toBe("200px"); }); - test("hears only its own frames, and only while the bot writes", () => { + test("hears only its own frames, and only while the agent writes", () => { const { view, frame } = draw(); say({ botloft: "cursor", view: VIEW, point: { x: 10, y: 10 }, box: null }, window); expect(view.container.querySelector(".bot-cursor")).toBeNull(); diff --git a/app/src/features/screens/screens.test.tsx b/app/src/features/screens/screens.test.tsx index 88a98870..d7fc9733 100644 --- a/app/src/features/screens/screens.test.tsx +++ b/app/src/features/screens/screens.test.tsx @@ -29,7 +29,7 @@ const frames = (within_: HTMLElement) => [...within_.querySelectorAll("iframe")].map((frame) => frame.getAttribute("src")); describe("design area", () => { - test("says when the bot has made no screens", async () => { + test("says when the agent has made no screens", async () => { const { fake } = crew(); await openScout(fake); fireEvent.click(toggle()); diff --git a/app/src/features/terminal/terminal.test.tsx b/app/src/features/terminal/terminal.test.tsx index da1fc871..51dd595b 100644 --- a/app/src/features/terminal/terminal.test.tsx +++ b/app/src/features/terminal/terminal.test.tsx @@ -36,7 +36,7 @@ const panel = (name: string) => screen.getByRole("complementary", { name }); const dock = (name: string) => within(screen.getByRole("navigation", { name: "Computer" })).getByRole("button", { name }); -describe("the bot's terminal", () => { +describe("the agent's terminal", () => { test("a command in the chat opens it, with each command, what it is for and what it printed", async () => { const { fake, scout } = await setup(); fireEvent.click(screen.getByRole("button", { name: "Show in terminal: npm test" })); diff --git a/app/src/features/updates/updates.test.tsx b/app/src/features/updates/updates.test.tsx index 56d0b292..713ae142 100644 --- a/app/src/features/updates/updates.test.tsx +++ b/app/src/features/updates/updates.test.tsx @@ -25,13 +25,13 @@ describe("app updates", () => { test("a new version installs in one click, in plain words", async () => { const host = new FakeHost(); const update = new FakeUpdate("0.2.0"); - update.notes = "Bots start faster."; + update.notes = "Agents start faster."; host.update = update; renderApp(host); fireEvent.click(await screen.findByRole("button", { name: "Update available" })); const dialog = screen.getByRole("dialog", { name: "Update Botloft" }); expect(dialog.textContent).toContain("Botloft 0.2.0 is ready"); - expect(dialog.textContent).toContain("Bots start faster."); + expect(dialog.textContent).toContain("Agents start faster."); fireEvent.click(within(dialog).getByRole("button", { name: "Update now" })); expect(await within(dialog).findByText("Downloading… 50%")).toBeDefined(); expect(update.installs).toBe(1); diff --git a/app/src/i18n/en/account.ts b/app/src/i18n/en/account.ts index 0e280e94..47c574aa 100644 --- a/app/src/i18n/en/account.ts +++ b/app/src/i18n/en/account.ts @@ -15,8 +15,9 @@ export const account = { }, usage: { title: "Usage", - intro: "How much of your Claude plan your bots have used. It updates whenever a bot works.", - empty: "Usage shows up after a bot's first reply.", + intro: + "How much of your Claude plan your agents have used. It updates whenever an agent works.", + empty: "Usage shows up after an agent's first reply.", windows: { five_hour: "5-hour window", seven_day: "This week", @@ -25,12 +26,12 @@ export const account = { } as Record, used: (percent: number) => `${percent}% used`, resets: (relative: string) => `Resets ${relative}`, - limited: "Limit reached. Your bots wait until it resets.", + limited: "Limit reached. Your agents wait until it resets.", updated: (relative: string) => `Updated ${relative}`, tokens: { - title: "Use by bot", + title: "Use by agent", intro: - "How much of your weekly plan each bot used, and the tokens behind it: the pieces of text a bot reads and writes.", + "How much of your weekly plan each agent used, and the tokens behind it: the pieces of text an agent reads and writes.", period: "Period", periods: { hour: "Last hour", @@ -39,25 +40,25 @@ export const account = { month: "30 days", all: "All time", }, - empty: "No bot worked in this period.", + empty: "No agent worked in this period.", failed: "Could not load the tokens", archived: "archived", /** `crew` is null on the total of all bots. */ detail: (crew: string | null, times: number) => `${crew === null ? "" : `${crew} · `}Worked ${times === 1 ? "once" : `${times} times`}`, - total: "All bots", + total: "All agents", share: (percent: string) => `≈ ${percent} of the week`, tokensUsed: (count: string) => `${count} tokens`, estimate: - "≈ Estimated: Botloft learns how much of your plan the bots' work takes from how the weekly plan rises while they work. What you use outside Botloft also raises it, so the bots may show a little more than they used.", + "≈ Estimated: Botloft learns how much of your plan the agents' work takes from how the weekly plan rises while they work. What you use outside Botloft also raises it, so the agents may show a little more than they used.", learning: - "Each bot's share of your weekly plan shows up once the plan has gone up a few points while the bots work. Until then, the tokens.", + "Each agent's share of your weekly plan shows up once the plan has gone up a few points while the agents work. Until then, the tokens.", }, }, backup: { exportTitle: "Save a copy", exportIntro: - "Keeps your crews, bots, chats and each bot's memory in one file locked with a passphrase, to bring back after a new install or on another computer. Work folders you chose outside Botloft are not in it.", + "Keeps your crews, agents, chats and each agent's memory in one file locked with a passphrase, to bring back after a new install or on another computer. Work folders you chose outside Botloft are not in it.", passphrase: "Passphrase", passphraseHint: "At least 8 characters. Without it the copy cannot be opened, and Botloft does not keep it anywhere.", @@ -79,7 +80,7 @@ export const account = { opening: "Opening…", from: (when: string) => `Copy from ${when}`, crew: (name: string, bots: number) => - `${name}: ${bots === 0 ? "no bots" : bots === 1 ? "1 bot" : `${bots} bots`}`, + `${name}: ${bots === 0 ? "no agents" : bots === 1 ? "1 agent" : `${bots} agents`}`, workFolders: "These work folders are not in the copy. Put their files back yourself if this is another computer:", replaces: "Everything in this Botloft is replaced. Botloft restarts to do it.", @@ -89,7 +90,7 @@ export const account = { openFailed: "Could not open the copy", restoreFailed: "Could not restore the copy", lightNote: - "This copy has the crews, the bots, their memory and routines. The chats and files are not in it.", + "This copy has the crews, the agents, their memory and routines. The chats and files are not in it.", reasons: { short_passphrase: "The passphrase needs at least 8 characters.", wrong_passphrase: "The passphrase is wrong, or the file is damaged.", @@ -116,7 +117,7 @@ export const account = { usage: (used: string, quota: string) => `${used} of ${quota} used`, uploadTitle: "Send a copy to the cloud", uploadIntro: - "Goes up: your crews, bots, each bot's memory and routines. Chats and files stay only on this computer. The copy is locked with a passphrase that never leaves it.", + "Goes up: your crews, agents, each agent's memory and routines. Chats and files stay only on this computer. The copy is locked with a passphrase that never leaves it.", upload: "Send a copy now", uploading: "Sending…", uploaded: (size: string) => `Copy sent (${size}).`, @@ -178,10 +179,10 @@ export const account = { mobile: { title: "Phone", intro: - "Approve your bots' requests, answer their questions and talk with them from anywhere. Files stay only on this computer.", + "Approve your agents' requests, answer their questions and talk with them from anywhere. Files stay only on this computer.", needAccount: "Sign in to the account above to use the phone.", warning: - "Whoever holds a connected phone, unlocked, can read your bots' conversations, write to them and allow requests until you disconnect it. A PIN on the phone helps; you can turn it on there.", + "Whoever holds a connected phone, unlocked, can read your agents' conversations, write to them and allow requests until you disconnect it. A PIN on the phone helps; you can turn it on there.", connect: "Connect a phone", starting: "Preparing…", qrLabel: "Code to scan with your phone", @@ -208,55 +209,55 @@ export const account = { title: "Settings", background: "In the background", keepWorking: "Keep working after you close Botloft", - keepWorkingOn: "Your bots go on working and answering after you close this window.", + keepWorkingOn: "Your agents go on working and answering after you close this window.", keepWorkingOff: - "Closing Botloft stops every bot. They pick up where they left off when you open it again.", + "Closing Botloft stops every agent. They pick up where they left off when you open it again.", tray: "Show Botloft near the clock", trayOn: - "With the window closed, its icon stays near the clock: a click opens Botloft, and it tells you when a bot needs you.", + "With the window closed, its icon stays near the clock: a click opens Botloft, and it tells you when an agent needs you.", trayOff: - "Closing the window closes Botloft. The bots keep working, with no icon or notifications.", + "Closing the window closes Botloft. The agents keep working, with no icon or notifications.", startWithSystem: (system: string) => `Start with ${system}`, startWithSystemOn: (system: string) => - `When you sign in to ${system}, your bots get back to work on their own, without opening this window.`, - startWithSystemOff: "After you restart the computer, the bots wait until you open Botloft.", + `When you sign in to ${system}, your agents get back to work on their own, without opening this window.`, + startWithSystemOff: "After you restart the computer, the agents wait until you open Botloft.", openAtSignIn: (system: string) => `Open the window when you sign in to ${system}`, openAtSignInOn: (system: string) => `Botloft's window opens when you sign in to ${system}.`, openAtSignInNearClock: "Botloft starts near the clock, without opening the window.", openAtSignInOff: "The window opens only when you open Botloft.", alerts: "Notifications", - notifyNeeds: "When a bot needs you", + notifyNeeds: "When an agent needs you", notifyNeedsHint: (system: string) => - `A ${system} notification when a bot asks for permission or needs you to sign in, if Botloft is not in front.`, - notifyDone: "When a bot finishes", - notifyDoneHint: "A notification when a bot finishes what it was doing.", - markReplies: "Mark the icon when a bot replies", + `A ${system} notification when an agent asks for permission or needs you to sign in, if Botloft is not in front.`, + notifyDone: "When an agent finishes", + notifyDoneHint: "A notification when an agent finishes what it was doing.", + markReplies: "Mark the icon when an agent replies", markRepliesHint: "A dot on the Botloft icon in the taskbar while a conversation is unread.", sound: "Play a sound", soundHint: "A short sound with each notification, also with Botloft in front.", appSounds: "App sounds", appSoundsHint: - "Soft sounds while Botloft is in front: a message sent, a reply or a file in the open chat, a new bot or crew.", + "Soft sounds while Botloft is in front: a message sent, a reply or a file in the open chat, a new agent or crew.", alertsNeedTray: "With the window closed, notifications only come with Botloft near the clock (in General).", - keepAwake: "Keep the computer awake while bots work", + keepAwake: "Keep the computer awake while agents work", keepAwakeHint: "It still sleeps when you close the lid or choose Sleep.", saveFailed: "Could not change the setting", pages: "Parts of Settings", general: "General", - defaultAgent: "Agent for new bots", + defaultAgent: "New agents run on", defaultAgentHint: - "Used for new bots, the chief of a new crew and the bots a template adds. Each bot keeps the agent it was made with.", + "Used for new agents, the chief of a new crew and the agents a template adds. Each agent keeps the engine it was made with.", chat: "Chat", enterSends: "Enter sends the message", enterSendsOn: "Shift+Enter starts a new line.", enterSendsOff: "Enter starts a new line, and Ctrl+Enter sends.", - followBot: "Open the browser and screens when a bot starts using them", - followBotOn: "The panel opens beside the chat, so you see what the bot does.", + followBot: "Open the browser and screens when an agent starts using them", + followBotOn: "The panel opens beside the chat, so you see what the agent does.", followBotOff: "The panel's button gets a dot, and you open it when you want.", approvalWait: "How long a request for permission waits for you", approvalWaitHint: - "With no answer by then, the bot's request is denied and it goes on without it.", + "With no answer by then, the agent's request is denied and it goes on without it.", waitFor: (minutes: number) => minutes < 60 || minutes % 60 !== 0 ? `${minutes} min` @@ -274,13 +275,13 @@ export const account = { language: "Language", archived: "Archived", archivedIntro: - "Bots and crews you archived are stopped and out of sight, and Botloft still keeps their conversations. Delete one to remove it for good.", + "Agents and crews you archived are stopped and out of sight, and Botloft still keeps their conversations. Delete one to remove it for good.", archivedEmpty: "Nothing is archived.", archivedLoadFailed: "Could not load what is archived", /** `when` is relative, like "2 days ago". */ archivedCrew: (bots: number, when: string) => - `Crew · ${bots === 1 ? "1 bot" : `${bots} bots`} · archived ${when}`, - archivedBot: (crew: string, when: string) => `Bot in ${crew} · archived ${when}`, + `Crew · ${bots === 1 ? "1 agent" : `${bots} agents`} · archived ${when}`, + archivedBot: (crew: string, when: string) => `Agent in ${crew} · archived ${when}`, deleteArchived: "Delete", deleteArchivedOne: (name: string) => `Delete ${name}`, about: "About", diff --git a/app/src/i18n/en/alerts.ts b/app/src/i18n/en/alerts.ts index 9f4fe7c6..363e65d7 100644 --- a/app/src/i18n/en/alerts.ts +++ b/app/src/i18n/en/alerts.ts @@ -3,10 +3,10 @@ export const alerts = { tray: { tooltip: (status: string) => `Botloft: ${status}`, - working: (count: number) => (count === 1 ? "1 bot working" : `${count} bots working`), + working: (count: number) => (count === 1 ? "1 agent working" : `${count} agents working`), needsYou: (bot: string) => `${bot} needs you`, waiting: "Something is waiting for you", - idle: "No bot working", + idle: "No agent working", open: "Open Botloft", pause: "Pause every crew", resume: "Resume every crew", diff --git a/app/src/i18n/en/bots.ts b/app/src/i18n/en/bots.ts index 119fdef9..24330e33 100644 --- a/app/src/i18n/en/bots.ts +++ b/app/src/i18n/en/bots.ts @@ -19,7 +19,7 @@ export const bots = { }, auth_error: { label: "Sign-in needed", - hint: "Claude Code is not signed in, or the account can't be used. Sign in to Claude and the bot starts again by itself. Already signed in? Check your Claude plan, then restart the bot.", + hint: "Claude Code is not signed in, or the account can't be used. Sign in to Claude and the agent starts again by itself. Already signed in? Check your Claude plan, then restart the agent.", }, backoff: { label: "Restarting", @@ -31,7 +31,7 @@ export const bots = { }, chief: { badge: "Chief", - hint: (crew: string) => `Leads ${crew}: plans the work and suggests new bots`, + hint: (crew: string) => `Leads ${crew}: plans the work and suggests new agents`, }, header: { noRole: "No role", @@ -40,7 +40,7 @@ export const bots = { restart: "Restart", showDetails: "Show details", hideDetails: "Hide details", - more: "More bot actions", + more: "More agent actions", menuOf: (name: string) => `Actions for ${name}`, edit: "Edit", restartFresh: "Restart with a new conversation", @@ -49,16 +49,16 @@ export const bots = { stopChief: "Stop being chief", markUnread: "Mark as unread", markRead: "Mark as read", - archive: "Archive bot", - delete: "Delete bot", + archive: "Archive agent", + delete: "Delete agent", failed: { - pause: "Could not pause the bot", - resume: "Could not resume the bot", - restart: "Could not restart the bot", + pause: "Could not pause the agent", + resume: "Could not resume the agent", + restart: "Could not restart the agent", openFolder: "Could not open the folder", chief: "Could not change the chief", - archive: "Could not archive the bot", - delete: "Could not delete the bot", + archive: "Could not archive the agent", + delete: "Could not delete the agent", }, fresh: { title: "Start a new conversation?", @@ -68,12 +68,12 @@ export const bots = { }, archiveConfirm: { title: (name: string) => `Archive ${name}?`, - confirm: "Archive bot", - body: "The bot stops and leaves the crew. Messages still waiting for it are not delivered.", + confirm: "Archive agent", + body: "The agent stops and leaves the crew. Messages still waiting for it are not delivered.", }, deleteConfirm: { title: (name: string) => `Delete ${name}?`, - confirm: "Delete bot", + confirm: "Delete agent", /** `running` is false for an archived bot, which stopped long ago. */ removed: (name: string, running: boolean) => `${name} ${running ? "stops now and leaves" : "leaves"} Botloft for good, with its conversation, its routines and the tasks it was part of. This can't be undone.`, @@ -85,43 +85,44 @@ export const bots = { }, }, dialog: { - newTitle: "New bot", + newTitle: "New agent", editTitle: (name: string) => `Edit ${name}`, - create: "Create bot", + create: "Create agent", save: "Save", name: "Name", namePlaceholder: "Reviewer", - nameHint: "Other bots reach it by the handle made from this name.", + nameHint: "Other agents reach it by the handle made from this name.", role: "Role", rolePlaceholder: "Reviews pull requests before they merge", instructions: "Instructions", - instructionsPlaceholder: "How this bot works, what it may do on its own and when to ask.", - instructionsHint: "Saved to the bot's rules now; the bot reads them the next time it starts.", + instructionsPlaceholder: "How this agent works, what it may do on its own and when to ask.", + instructionsHint: + "Saved to the agent's rules now; the agent reads them the next time it starts.", fullAccess: { title: "Run any command without asking", off: "Off: it may run only the commands listed below.", - on: "On: it runs any command without asking you, and a command can read or change anything you can. Only for a bot you trust.", + on: "On: it runs any command without asking you, and a command can read or change anything you can. Only for an agent you trust.", }, commands: { - title: "Commands this bot may run", + title: "Commands this agent may run", placeholder: "git status\nnpm test", - hint: "One command per line; it may run these and anything that starts with them, without asking you. It cannot ask, so everything else is refused. A single * lets it run anything. The bot restarts to use the list.", + hint: "One command per line; it may run these and anything that starts with them, without asking you. It cannot ask, so everything else is refused. A single * lets it run anything. The agent restarts to use the list.", }, agent: { - title: "Agent", + title: "Runs on", names: { claude: "Claude Code", agy: "Antigravity (experimental)", codex: "Codex", }, experimental: - "Experimental. This bot cannot ask you before it acts. It can work with files in its own folders and use the crew's tools, but it cannot run commands, and it has no usage meter or model choice.", + "Experimental. This agent cannot ask you before it acts. It can work with files in its own folders and use the crew's tools, but it cannot run commands, and it has no usage meter or model choice.", experimentalCodex: - "Experimental. This bot asks you before it changes a file or runs a command, like Claude Code. It can still read any file.", + "Experimental. This agent asks you before it changes a file or runs a command, like Claude Code. It can still read any file.", }, color: "Color", swatch: (color: string) => `Color ${color}`, - colorUnset: "Left unset, the bot gets the crew's next color.", + colorUnset: "Left unset, the agent gets the crew's next color.", custom: "Pick any color", picker: { area: "Saturation and brightness", @@ -134,7 +135,7 @@ export const bots = { }, notices: { crewPaused: (crew: string) => `${crew} is paused`, - crewPausedBody: "Its bots stay stopped until you resume the crew.", + crewPausedBody: "Its agents stay stopped until you resume the crew.", }, details: { title: (name: string) => `About ${name}`, diff --git a/app/src/i18n/en/browser.ts b/app/src/i18n/en/browser.ts index 3aa5a6f1..070a8c1b 100644 --- a/app/src/i18n/en/browser.ts +++ b/app/src/i18n/en/browser.ts @@ -31,7 +31,7 @@ export const browser = { "When it searches or uses a site, you'll see it here live: every page it opens, every click.", starting: "Opening the browser…", closed: "Browser closed", - closedBody: "It opens again when the bot needs it. Logins stay.", + closedBody: "It opens again when the agent needs it. Logins stay.", openForMe: "Open it for me", openForMeWhy: (bot: string) => `To see what ${bot} left open and use it yourself. ${bot} is not woken or told; give it back when you are done and it rests again.`, @@ -39,8 +39,8 @@ export const browser = { failedTitle: "The browser couldn't open", failedBody: (system: string): string => system === "Windows" - ? "Botloft uses Microsoft Edge, which comes with Windows. Check that it's installed, then ask the bot to try again." - : "Botloft uses Google Chrome, Chromium or Microsoft Edge. Check that one of them is installed, then ask the bot to try again.", + ? "Botloft uses Microsoft Edge, which comes with Windows. Check that it's installed, then ask the agent to try again." + : "Botloft uses Google Chrome, Chromium or Microsoft Edge. Check that one of them is installed, then ask the agent to try again.", details: "Details", tabs: { label: "Tabs", @@ -130,7 +130,7 @@ export const browser = { help: { needs: (bot: string) => `${bot} needs you in the browser`, asks: (bot: string, task: string) => `${bot} asks: ${task}`, - why: "Take control, do it on the page yourself and give it back. If the site won't let you sign in there, use Sign in in a window in the browser panel. The bot doesn't see what you type in password fields.", + why: "Take control, do it on the page yourself and give it back. If the site won't let you sign in there, use Sign in in a window in the browser panel. The agent doesn't see what you type in password fields.", take: "Take the browser", done: "Done", wontDo: "I won't do it", diff --git a/app/src/i18n/en/catalog.ts b/app/src/i18n/en/catalog.ts index bdc83de1..923ad1c2 100644 --- a/app/src/i18n/en/catalog.ts +++ b/app/src/i18n/en/catalog.ts @@ -19,17 +19,17 @@ import { security } from "./catalogSecurity"; // each. Plain words: "bot" and "crew", never "template" or "prompt". export const catalog = { - title: "Bot agency", - open: "Bot agency", - openHint: "Ready-made bots you can add to this crew", + title: "Agent agency", + open: "Agent agency", + openHint: "Ready-made agents you can add to this crew", intro: - "Pick a bot for the job. It joins this crew ready to work, and you can change anything about it afterwards.", + "Pick an agent for the job. It joins this crew ready to work, and you can change anything about it afterwards.", invite: { title: "Who do you want on your crew?", - body: "Not sure what to create? Pick a bot for the job. You can change anything about it afterwards.", + body: "Not sure what to create? Pick an agent for the job. You can change anything about it afterwards.", }, - search: "Search bots", - filter: "Kinds of bots", + search: "Search agents", + filter: "Kinds of agents", categories: { all: "All", code: "Code", @@ -47,23 +47,23 @@ export const catalog = { games: "Games", legal: "Legal", }, - none: "No bot matches that.", - loading: "Loading the bots…", + none: "No agent matches that.", + loading: "Loading the agents…", add: "Add to crew", learnMore: "Learn more", - back: "Back to all bots", - seeAll: "See all bots", + back: "Back to all agents", + seeAll: "See all agents", joined: (name: string) => `${name} joined the crew`, customize: "Customize", failed: { - load: "Could not load the bots", - add: "Could not add the bot", + load: "Could not load the agents", + add: "Could not add the agent", }, detail: { what: "What it does", when: "When to call it", pairs: "Works well with", - technical: "What the bot is told when it starts", + technical: "What the agent is told when it starts", }, roles: { ...base, diff --git a/app/src/i18n/en/catalogBase.ts b/app/src/i18n/en/catalogBase.ts index 1d4c16e7..938e59db 100644 --- a/app/src/i18n/en/catalogBase.ts +++ b/app/src/i18n/en/catalogBase.ts @@ -13,10 +13,10 @@ export const base = { }, "code-reviewer": { name: "Code Reviewer", - role: "Reviews what other bots changed and points out bugs, risks and needless complexity", + role: "Reviews what other agents changed and points out bugs, risks and needless complexity", summary: "Reads changes with a critical eye and says what is wrong or risky", about: - "Reads the changes another bot made and lists what could break, what is risky and what is more complicated than it needs to be. It never edits the code itself, it only reports, so you decide. It thinks harder than most bots, so it uses a bit more of your plan.", + "Reads the changes another agent made and lists what could break, what is risky and what is more complicated than it needs to be. It never edits the code itself, it only reports, so you decide. It thinks harder than most agents, so it uses a bit more of your plan.", when: "After a Developer changes something that matters: a payment, a login, anything hard to undo.", pairs: "Developer, whose work it reviews, and QA Tester.", }, diff --git a/app/src/i18n/en/catalogDesign.ts b/app/src/i18n/en/catalogDesign.ts index fe510eaa..0c746b66 100644 --- a/app/src/i18n/en/catalogDesign.ts +++ b/app/src/i18n/en/catalogDesign.ts @@ -7,7 +7,7 @@ export const design = { summary: "Writes a short brand guide from your material and checks that everything follows it", about: "Gathers your logo, colors, fonts and examples into a one-page brand guide, marking what it saw and what it suggests. Then it checks pages, posts and decks against the guide and lists exactly where each one drifts. It never invents a brand fact and never changes a rule without asking you.", - when: "When you have a brand and several people or bots make things for it.", + when: "When you have a brand and several people or agents make things for it.", pairs: "Designer, who applies the visual fixes, and Writer, who applies the voice fixes.", }, "ui-designer": { diff --git a/app/src/i18n/en/catalogEngineering.ts b/app/src/i18n/en/catalogEngineering.ts index ea186673..0468b4ab 100644 --- a/app/src/i18n/en/catalogEngineering.ts +++ b/app/src/i18n/en/catalogEngineering.ts @@ -62,7 +62,7 @@ export const engineering = { role: "Reviews your own code and setup for security weaknesses and explains how to fix them", summary: "Looks for security holes in your own code and setup and explains how to fix them", about: - "Reviews your own code and setup for weaknesses, explains each one in plain words with how serious it is and the smallest fix. It works defensively, only on what is yours, and never repeats a secret it finds. It thinks harder than most bots, so it uses a bit more of your plan.", + "Reviews your own code and setup for weaknesses, explains each one in plain words with how serious it is and the smallest fix. It works defensively, only on what is yours, and never repeats a secret it finds. It thinks harder than most agents, so it uses a bit more of your plan.", when: "Before you launch, after a big change, or when you handle other people's data.", pairs: "Developer and DevOps Engineer, who apply the fixes, and Code Reviewer.", }, diff --git a/app/src/i18n/en/catalogProduct.ts b/app/src/i18n/en/catalogProduct.ts index 55b458ce..945a3f1a 100644 --- a/app/src/i18n/en/catalogProduct.ts +++ b/app/src/i18n/en/catalogProduct.ts @@ -46,8 +46,8 @@ export const product = { summary: "Keeps the crew's work moving in small steps, with regular check-ins and honest looks back", about: - "Keeps a simple board of what is next, in progress and done, asks the bots for quick updates and leads a look back at the end of each round to change one thing.", - when: "When the crew has many bots and work keeps getting stuck or forgotten.", + "Keeps a simple board of what is next, in progress and done, asks the agents for quick updates and leads a look back at the end of each round to change one thing.", + when: "When the crew has many agents and work keeps getting stuck or forgotten.", pairs: "Project Manager, for the plan, and Meeting Secretary.", }, "goals-coach": { diff --git a/app/src/i18n/en/chat.ts b/app/src/i18n/en/chat.ts index ca5657b2..3ae54993 100644 --- a/app/src/i18n/en/chat.ts +++ b/app/src/i18n/en/chat.ts @@ -43,13 +43,13 @@ export const chat = { showInFolder: (file: string) => `Show ${file} in its folder`, showInFolderHint: "Show in folder", openFolderFailed: "Could not open the folder", - missing: "No longer in the bot's folder", + missing: "No longer in the agent's folder", loading: (file: string) => `Loading ${file}`, }, inbound: { task: "Task", result: "Result", - goneBot: "A bot no longer in the crew", + goneBot: "An agent no longer in the crew", routine: "Show what the routine asks", }, run: { @@ -60,7 +60,7 @@ export const chat = { `Took ${time}${ tokens === null ? "" : `. Read ${tokens.read} tokens and wrote ${tokens.wrote}.` }`, - stopped: (reason: string) => `The bot stopped working on this: ${reason}`, + stopped: (reason: string) => `The agent stopped working on this: ${reason}`, }, tools: { label: "Tool calls", @@ -120,7 +120,7 @@ export const chat = { `${bot} did not say what this command is for. If you are not sure, deny it and ask.`, command: "See the command", noteLabel: (bot: string) => `Note for ${bot} if you deny`, - notePlaceholder: "Why not? Sent to the bot if you deny (optional)", + notePlaceholder: "Why not? Sent to the agent if you deny (optional)", allow: "Allow", deny: "Deny", /** "Allow always" (spec 10.1), by what it covers. */ @@ -163,14 +163,14 @@ export const chat = { bypassBody: (bot: string) => `${bot} will edit files, run commands and use the internet on this computer without asking you first.`, bypassRisk: - "It is not limited to its folder: it can read and change your other files, other bots' files and Botloft's own files. A message from someone else could get it to do something you did not want.", + "It is not limited to its folder: it can read and change your other files, other agents' files and Botloft's own files. A message from someone else could get it to do something you did not want.", bypassChief: (bot: string) => - `${bot} leads its crew, so it will also create new bots without asking you.`, + `${bot} leads its crew, so it will also create new agents without asking you.`, bypassAdvice: - "Turn this on only for a bot you trust with everything, and only while you need it.", + "Turn this on only for an agent you trust with everything, and only while you need it.", badge: "Asks nothing", badgeHint: - "This bot does everything without asking. Change it in the mode picker below the chat.", + "This agent does everything without asking. Change it in the mode picker below the chat.", }, model: { title: "Model", @@ -205,7 +205,7 @@ export const chat = { loading: "Loading the models…", listFailed: "Could not load the list of models", failed: "Could not change the model", - defaultHint: (bot: string) => `${bot} uses the agent's own default model`, + defaultHint: (bot: string) => `${bot} uses the engine's own default model`, later: (bot: string, model: string) => `${bot} switches to ${model}.`, }, effort: { @@ -254,7 +254,7 @@ export const chat = { noAuto: "It does not compact by itself.", byItself: "Codex compacts it by itself when it gets close to full.", estimate: - "An estimate: this agent does not say how large its window is, so the size comes from its model family.", + "An estimate: this engine does not say how large its window is, so the size comes from its model family.", compact: "Compact now", compactHint: (bot: string) => `Replaces what came before with a summary, so ${bot} has room again and each message uses less.`, @@ -264,7 +264,7 @@ export const chat = { failed: "Could not compact the conversation", }, suggestion: { - title: (bot: string) => `${bot} suggests a new bot`, + title: (bot: string) => `${bot} suggests a new agent`, why: "Why", name: "Name", role: "Role", @@ -273,9 +273,9 @@ export const chat = { startsNow: (bot: string) => `It starts right away and gets its work from ${bot}.`, noteLabel: (bot: string) => `What to tell ${bot} if you say no`, notePlaceholder: (bot: string) => `If you say no, tell ${bot} why (optional)`, - create: "Create bot", + create: "Create agent", decline: "Not now", - createFailed: "Could not create the bot", + createFailed: "Could not create the agent", declineFailed: "Could not send the answer", created: (name: string) => `You created ${name}`, declined: (name: string) => `You said no to ${name}`, @@ -341,7 +341,7 @@ export const chat = { read: (whom: string) => `Read the files of ${whom} and of its work folder`, edit: (whom: string) => `Change and add files of ${whom} and of its work folder`, why: (bot: string) => `Why, says ${bot}:`, - explain: "Crews do not see each other. Only now lasts until the bot ends this turn.", + explain: "Crews do not see each other. Only now lasts until the agent ends this turn.", once: "Only now", alwaysBot: (other: string) => `Always ${other}`, alwaysCrew: (crew: string) => `Always the crew ${crew}`, @@ -363,7 +363,7 @@ export const chat = { reason: (bot: string) => `Why, says ${bot}:`, newInstructions: "See the new instructions", explain: - "The new role, instructions, model and effort count from the bot's next start, once it finishes what it's doing.", + "The new role, instructions, model and effort count from the agent's next start, once it finishes what it's doing.", apply: "Change", decline: "Not now", noteLabel: (bot: string) => `What to tell ${bot} if you say no`, @@ -376,7 +376,7 @@ export const chat = { plan: { ready: (bot: string) => `${bot} made a plan and wants to go ahead`, noteLabel: (bot: string) => `What ${bot} should change in the plan`, - notePlaceholder: "What should change? Sent to the bot if you keep planning (optional)", + notePlaceholder: "What should change? Sent to the agent if you keep planning (optional)", approve: "Approve plan", keepPlanning: "Keep planning", approveFailed: "Could not approve the plan", @@ -394,8 +394,8 @@ export const chat = { "Claude Code is not signed in, or the account can't be used right now. Sign in to Claude, or check your Claude plan.", usageLimit: "Your Claude plan reached its usage limit. Messages wait until it resets.", modelUnavailable: - "This bot's model isn't available: it may not be on your Claude plan. Pick another model below the chat.", - turnFailed: (detail: string) => `The bot couldn't finish this: ${detail}`, + "This agent's model isn't available: it may not be on your Claude plan. Pick another model below the chat.", + turnFailed: (detail: string) => `The agent couldn't finish this: ${detail}`, compacted: "The conversation was compacted: what came before is now a summary, and there is room again.", autoCompacted: diff --git a/app/src/i18n/en/connections.ts b/app/src/i18n/en/connections.ts index 8c3c75ed..b18b45f5 100644 --- a/app/src/i18n/en/connections.ts +++ b/app/src/i18n/en/connections.ts @@ -4,12 +4,12 @@ export const connections = { title: "Connected tools", intro: - "Tools of your own that your bots can use besides the ones Botloft gives them, like a LinkedIn reader or a system of your company. Each bot gets only the ones you turn on for it, and it still asks you before using them.", + "Tools of your own that your agents can use besides the ones Botloft gives them, like a LinkedIn reader or a system of your company. Each agent gets only the ones you turn on for it, and it still asks you before using them.", loadFailed: "Could not read the connected tools", none: "No tools connected yet.", add: "Connect a tool…", usedBy: (names: string) => `Used by ${names}`, - unused: "No bot uses it yet", + unused: "No agent uses it yet", kinds: { stdio: "A program on this computer", http: "A tool at an address", @@ -23,12 +23,12 @@ export const connections = { name: "Name", nameHint: "Only needed when the settings you pasted come without a name.", purpose: "What is it for? (optional)", - purposeHint: "Your bots read this to know when to use it.", + purposeHint: "Your agents read this to know when to use it.", found: (count: number) => (count === 1 ? "Found 1 tool." : `Found ${count} tools.`), program: (name: string) => - `${name} is a program. It will run on your computer with your permissions, outside the folders Botloft keeps for your bots. Only connect programs you trust.`, + `${name} is a program. It will run on your computer with your permissions, outside the folders Botloft keeps for your agents. Only connect programs you trust.`, address: (name: string) => - `${name} is a tool at an address. What your bot sends to it leaves this computer.`, + `${name} is a tool at an address. What your agent sends to it leaves this computer.`, command: "What it runs", understood: "I understand and I trust it", connect: "Connect", @@ -55,7 +55,7 @@ export const connections = { }, removing: { title: (name: string) => `Remove ${name}?`, - text: "Bots that use it lose it and start again when they are free. What you allowed them to do with it for good is forgotten.", + text: "Agents that use it lose it and start again when they are free. What you allowed them to do with it for good is forgotten.", confirm: "Remove", failed: "Could not remove the tool", }, @@ -63,7 +63,7 @@ export const connections = { title: "Connected tools", none: "No tools connected yet. You can connect them in Settings, under Connected tools.", toggle: (tool: string, bot: string) => `${bot} can use ${tool}`, - failed: "Could not change the tools of this bot", + failed: "Could not change the tools of this agent", reason: "Why", }, states: { diff --git a/app/src/i18n/en/crewTemplates.ts b/app/src/i18n/en/crewTemplates.ts index f6648518..035f8e23 100644 --- a/app/src/i18n/en/crewTemplates.ts +++ b/app/src/i18n/en/crewTemplates.ts @@ -7,8 +7,8 @@ export const crewTemplates = { title: "Start from a template", intro: "Pick a team that is ready to work, or start with an empty crew.", scratch: "Start with an empty crew", - scratchHint: "Only the chief. You add the bots yourself.", - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), + scratchHint: "Only the chief. You add the agents yourself.", + bots: (count: number) => (count === 1 ? "1 agent" : `${count} agents`), /** Above the list of bots a team adds. */ adds: "This team adds", change: "Pick another template", @@ -16,8 +16,8 @@ export const crewTemplates = { /** After the crew is made but some bots could not be added. */ partial: (failed: number) => failed === 1 - ? "The crew was created, but 1 bot could not be added. You can add it from the Bot agency." - : `The crew was created, but ${failed} bots could not be added. You can add them from the Bot agency.`, + ? "The crew was created, but 1 agent could not be added. You can add it from the Agent agency." + : `The crew was created, but ${failed} agents could not be added. You can add them from the Agent agency.`, loadFailed: "Could not load the templates", items: { "content-studio": { diff --git a/app/src/i18n/en/crews.ts b/app/src/i18n/en/crews.ts index ef8acad6..95b7629b 100644 --- a/app/src/i18n/en/crews.ts +++ b/app/src/i18n/en/crews.ts @@ -3,7 +3,7 @@ export const crews = { newCrew: "New crew", - newBot: "New bot", + newBot: "New agent", rename: "Rename", paused: "Paused", sidebar: { @@ -27,7 +27,7 @@ export const crews = { collapse: (crew: string) => `Fold ${crew}`, expand: (crew: string) => `Unfold ${crew}`, /** Beside a folded crew's name, when one of its bots needs the owner. */ - waiting: "a bot needs you", + waiting: "an agent needs you", filters: { label: "Filter conversations", all: "All", @@ -42,14 +42,14 @@ export const crews = { working: (count: number) => `${count} working`, waiting: (count: number) => (count === 1 ? "1 needs you" : `${count} need you`), calm: "All quiet", - noBots: "No bots yet", + noBots: "No agents yet", }, dialog: { color: "Color", noColor: "No color", swatch: (color: string) => `Color ${color}`, folder: "Work folder", - folderHint: "Where the bots put what they make. It can be a folder you already use.", + folderHint: "Where the agents put what they make. It can be a folder you already use.", folderDefault: "A new folder inside Botloft", chooseFolder: "Choose folder…", pickTitle: "Choose where the crew works", @@ -57,20 +57,20 @@ export const crews = { goal: "What is this crew for?", goalPlaceholder: "Build and keep up my bakery's website", goalHint: - "The crew starts with a chief, who reads this, plans the work and suggests the bots it needs.", + "The crew starts with a chief, who reads this, plans the work and suggests the agents it needs.", chiefModel: "Chief's model", chiefName: "Chief", - chiefRole: "Leads the crew: plans the work, suggests new bots and hands out tasks", + chiefRole: "Leads the crew: plans the work, suggests new agents and hands out tasks", renameTitle: "Rename crew", create: "Create crew", name: "Name", namePlaceholder: "Research", - createHint: "Bots in a crew can message each other and share a folder.", + createHint: "Agents in a crew can message each other and share a folder.", renameHint: (slug: string) => `The folder keeps its name (${slug}).`, }, view: { - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), - pausedNote: "paused: its bots stay stopped until you resume it", + bots: (count: number) => (count === 1 ? "1 agent" : `${count} agents`), + pausedNote: "paused: its agents stay stopped until you resume it", pause: "Pause crew", resume: "Resume crew", moreActions: "More crew actions", @@ -80,36 +80,36 @@ export const crews = { changeFolder: "Change work folder…", moveTitle: (crew: string) => `Move ${crew} to another folder?`, moveBody: (path: string) => - `The bots will work in ${path}. Each one restarts when it finishes what it's doing. Files already made stay where they are.`, + `The agents will work in ${path}. Each one restarts when it finishes what it's doing. Files already made stay where they are.`, move: "Move", archive: "Archive crew", delete: "Delete crew", tabs: { label: "Crew views", - bots: "Bots", + bots: "Agents", timeline: "Timeline", tasks: "Tasks", }, timelineEmpty: - "No messages yet. Write to a bot below; what the bots send each other shows up here too.", + "No messages yet. Write to an agent below; what the agents send each other shows up here too.", archiveTitle: (crew: string) => `Archive ${crew}?`, archiveBody: (bots: number) => bots === 0 ? "The crew leaves the app." - : `The crew leaves the app. ${bots === 1 ? "Its bot stops" : `Its ${bots} bots stop`}, and messages still waiting for them are not delivered.`, + : `The crew leaves the app. ${bots === 1 ? "Its agent stops" : `Its ${bots} agents stop`}, and messages still waiting for them are not delivered.`, deleteTitle: (crew: string) => `Delete ${crew}?`, /** `running` is false for an archived crew, which stopped long ago. */ deleteBody: (crew: string, bots: number, running: boolean) => bots === 0 ? `${crew} leaves Botloft for good. This can't be undone.` - : `${crew} and ${bots === 1 ? "its bot" : `its ${bots} bots`} ${running ? "stop now and leave" : "leave"} Botloft for good, with their conversations, routines and tasks. This can't be undone.`, + : `${crew} and ${bots === 1 ? "its agent" : `its ${bots} agents`} ${running ? "stop now and leave" : "leave"} Botloft for good, with their conversations, routines and tasks. This can't be undone.`, deleteKept: - "The folders stay on your computer, with everything in them: each bot's own folder and the crew's work folder:", + "The folders stay on your computer, with everything in them: each agent's own folder and the crew's work folder:", deleteRecycle: "Move the crew's folders to the Recycle Bin", deleteRecycled: - "The crew's folder goes to the Recycle Bin, with each bot's folder and the work folder in it. You can still get it back from there:", + "The crew's folder goes to the Recycle Bin, with each agent's folder and the work folder in it. You can still get it back from there:", deleteRecycledChosen: - "Each bot's own folder goes to the Recycle Bin, where you can still get it back. The work folder you chose stays where it is:", + "Each agent's own folder goes to the Recycle Bin, where you can still get it back. The work folder you chose stays where it is:", failed: { pause: "Could not pause the crew", resume: "Could not resume the crew", @@ -120,8 +120,9 @@ export const crews = { }, }, bots: { - empty: (crew: string) => `No bots in ${crew} yet.`, - emptyHint: "A bot is a Claude Code session that keeps running, with its own folder and role.", + empty: (crew: string) => `No agents in ${crew} yet.`, + emptyHint: + "An agent is a Claude Code session that keeps running, with its own folder and role.", noRole: "No role yet.", }, tasks: { @@ -129,8 +130,8 @@ export const crews = { open: "Open", all: "All", list: "Tasks", - noOpen: "No open tasks. Bots create tasks for each other with send_message.", - none: "No tasks yet. Bots create tasks for each other with send_message.", + noOpen: "No open tasks. Agents create tasks for each other with send_message.", + none: "No tasks yet. Agents create tasks for each other with send_message.", status: { open: "Open", done: "Done", @@ -138,7 +139,7 @@ export const crews = { cancelled: "Cancelled", expired: "Expired", }, - archivedBot: "an archived bot", + archivedBot: "an archived agent", /** Read between the requester and the assignee. */ asked: "asked", hop: (hops: number) => `hop ${hops}`, diff --git a/app/src/i18n/en/desktop.ts b/app/src/i18n/en/desktop.ts index fb36ee0d..557d8054 100644 --- a/app/src/i18n/en/desktop.ts +++ b/app/src/i18n/en/desktop.ts @@ -33,7 +33,7 @@ export const desktop = { stoppedBody: (bot: string) => `${bot} cannot read or use your apps until you let it go on.`, emptyTitle: (bot: string) => `${bot} has not used your desktop yet`, emptyBody: "When it reads or uses a window of an app you allowed, the window shows here, live.", - shortcut: "Ctrl+Alt+Esc stops every bot on your desktop, even with Botloft closed.", + shortcut: "Ctrl+Alt+Esc stops every agent on your desktop, even with Botloft closed.", waiting: "Waiting for the picture…", read: "Read the window", click: (target: string) => `Clicked "${target}"`, @@ -63,7 +63,7 @@ export const desktop = { realPoints: (bot: string) => [ `When accessibility cannot do something in this app, ${bot} moves your cursor and types as if it were you.`, `While it acts, your mouse and keyboard are its, and a notice on your screen says so.`, - `Move the mouse or press a key and it stops at once. Ctrl+Alt+Esc stops every bot on your desktop.`, + `Move the mouse or press a key and it stops at once. Ctrl+Alt+Esc stops every agent on your desktop.`, `It never types in password fields and never presses Windows' own keys, like the Windows key or Alt+Tab.`, ], realConfirm: "Turn on", diff --git a/app/src/i18n/en/messages.ts b/app/src/i18n/en/messages.ts index e644cce1..35df4f02 100644 --- a/app/src/i18n/en/messages.ts +++ b/app/src/i18n/en/messages.ts @@ -5,7 +5,7 @@ export const messages = { /** Bots calling each other, live. */ calls: { - label: "Bots calling each other", + label: "Agents calling each other", /** Before the call, for screen readers: the mascots show it. */ caller: (bot: string) => `${bot}:`, calling: (bot: string) => `Calling ${bot}`, @@ -14,15 +14,16 @@ export const messages = { composer: { messageTo: "Message to", recipient: "Recipient", - placeholder: (handle: string | undefined) => `Write to @${handle ?? "bot"}. Ctrl+Enter sends.`, + placeholder: (handle: string | undefined) => + `Write to @${handle ?? "agent"}. Ctrl+Enter sends.`, send: "Send", }, delivery: { delivered: "Delivered", read: "Read", - readTitle: "The bot began working on it", + readTitle: "The agent began working on it", delivering: "Delivering", - waiting: "Waiting for the bot", + waiting: "Waiting for the agent", retrying: (when: string, attempts: number) => `Retrying ${when}, after ${attempts} failed ${attempts === 1 ? "try" : "tries"}`, notDelivered: "Not delivered", @@ -34,14 +35,14 @@ export const messages = { title: "Messages not delivered", retryAll: "Retry all", explanation: - "Botloft stopped trying after several attempts. Retrying puts a message back in line; it goes out when the bot is ready.", + "Botloft stopped trying after several attempts. Retrying puts a message back in line; it goes out when the agent is ready.", to: (name: string) => `To ${name}`, tries: (count: number) => `${count} ${count === 1 ? "try" : "tries"}`, }, row: { you: "You", system: "Botloft", - goneBot: "a bot no longer in the crew", + goneBot: "an agent no longer in the crew", to: "to", task: "Task", result: "Result", diff --git a/app/src/i18n/en/onboarding.ts b/app/src/i18n/en/onboarding.ts index b2e955eb..422960e3 100644 --- a/app/src/i18n/en/onboarding.ts +++ b/app/src/i18n/en/onboarding.ts @@ -10,18 +10,18 @@ export const onboarding = { install: "Getting Botloft ready…", update: "Updating Botloft…", restart: "Restarting Botloft…", - start: "Starting your bots…", + start: "Starting your agents…", }, installNote: (system: string) => - `Botloft runs your bots in the background. In Settings you choose whether they keep working after you close this window and whether Botloft starts with ${system}.`, + `Botloft runs your agents in the background. In Settings you choose whether they keep working after you close this window and whether Botloft starts with ${system}.`, stopped: { title: "Botloft couldn't start", - body: "Botloft runs your bots in the background, and that part didn't start.", + body: "Botloft runs your agents in the background, and that part didn't start.", notRunning: "It is not running.", }, outdated: { title: "Botloft couldn't finish updating", - body: "The part that runs your bots in the background is still on the previous version.", + body: "The part that runs your agents in the background is still on the previous version.", running: (version: string) => `Running version ${version}.`, stillOld: (version: string) => `It still reports version ${version}.`, }, @@ -46,33 +46,33 @@ export const onboarding = { welcome: { title: "Welcome to Botloft", intro: - "A crew is a group of bots that keep running, message each other and share a folder. Each bot runs on the agent you pick: Claude Code, or another one you turn on. Each crew starts with a chief: tell it what the crew is for, and it plans the work and suggests the bots it needs.", + "A crew is a group of agents that keep running, message each other and share a folder. Each agent runs on the engine you pick: Claude Code, or another one you turn on. Each crew starts with a chief: tell it what the crew is for, and it plans the work and suggests the agents it needs.", botloft: "Botloft", running: (system: string) => - `Running in the background. It starts with ${system}, so your bots keep working after you close this window.`, + `Running in the background. It starts with ${system}, so your agents keep working after you close this window.`, runningNotAtStart: - "Running in the background, so your bots keep working after you close this window.", - runningWhileOpen: "Running while this window is open. Closing it stops your bots.", + "Running in the background, so your agents keep working after you close this window.", + runningWhileOpen: "Running while this window is open. Closing it stops your agents.", claudeCode: "Claude Code", checking: "Checking…", version: (version: string) => `Version ${version}`, account: "Claude account", signedIn: "Signed in.", - signedOut: "Your bots work with your Claude account. Sign in once and they are ready.", + signedOut: "Your agents work with your Claude account. Sign in once and they are ready.", ready: "ready", notReady: "not ready", stillChecking: "checking", - askFirstTitle: "Bots ask before they change things", + askFirstTitle: "Agents ask before they change things", askFirstBody: - "When a bot wants to run a command or edit a file, it asks in its chat and waits for you to allow or deny it.", + "When an agent wants to run a command or edit a file, it asks in its chat and waits for you to allow or deny it.", ideasTitle: "Or tap an idea", - claudeOptional: "Not installed. You only need it for bots that run on Claude Code.", + claudeOptional: "Not installed. You only need it for agents that run on Claude Code.", agentMissing: "Not found. Install it and open Botloft again.", notNeeded: "not needed", createCrew: "Create your first crew", }, claudeCode: { - help: "Botloft runs your bots with Claude Code. Install it or update it, open it once to sign in, and Botloft picks it up within 30 seconds.", + help: "Botloft runs your agents with Claude Code. Install it or update it, open it once to sign in, and Botloft picks it up within 30 seconds.", install: "How to install Claude Code", openFailed: "Could not open the link", }, diff --git a/app/src/i18n/en/phone.ts b/app/src/i18n/en/phone.ts index 81a06e7e..4e4e1a9d 100644 --- a/app/src/i18n/en/phone.ts +++ b/app/src/i18n/en/phone.ts @@ -5,7 +5,7 @@ export const phone = { pair: { title: "Connect this phone", intro: - "This phone will approve your bots' requests and answer their questions and talk with them, from anywhere. Files stay on your computer.", + "This phone will approve your agents' requests and answer their questions and talk with them, from anywhere. Files stay on your computer.", nameLabel: "Name of this phone", connect: "Connect", connecting: "Connecting…", @@ -22,7 +22,7 @@ export const phone = { inbox: { title: "Waiting for you", empty: "Nothing is waiting for you", - emptyBody: "When a bot needs you, it shows up here.", + emptyBody: "When an agent needs you, it shows up here.", loading: "Checking what is waiting…", computerOff: "Your computer is off or has no internet. Requests keep waiting there.", noConnection: "No connection. Trying again…", @@ -31,9 +31,9 @@ export const phone = { }, tabs: { requests: "Requests", chats: "Chats", newReply: "new reply" }, chats: { - empty: "No bots yet", - emptyBody: "When you create a bot on your computer, it shows up here.", - loading: "Loading the bots…", + empty: "No agents yet", + emptyBody: "When you create an agent on your computer, it shows up here.", + loading: "Loading the agents…", working: "working…", kind: { owner: "You", @@ -47,7 +47,7 @@ export const phone = { inCrew: (crew: string) => `in ${crew}`, allCrews: "All", crewsLabel: "Crews", - bots: (n: number) => `${n} ${n === 1 ? "bot" : "bots"}`, + bots: (n: number) => `${n} ${n === 1 ? "agent" : "agents"}`, back: "Chats", loadingChat: "Loading the conversation…", nothing: "Nothing here yet. Write the first message.", @@ -68,19 +68,19 @@ export const phone = { }, pending: { sending: "Sending…", - sent: "Sent. The bot will see it when it can.", + sent: "Sent. The agent will see it when it can.", notSent: "Not sent.", reasons: { offline: "Check the connection and try again.", timeout: "The computer did not answer. Check it is on and try again.", - invalid: "The bot cannot take this text.", - not_found: "This bot is gone.", + invalid: "The agent cannot take this text.", + not_found: "This agent is gone.", rate_limited: "Too many messages in a minute. Wait a bit.", failed: "Something went wrong. Try again.", } as Record, }, reach: - "A phone that is connected and unlocked can read your bots' conversations and write to them.", + "A phone that is connected and unlocked can read your agents' conversations and write to them.", }, approval: { asks: (bot: string) => `${bot} asks to`, @@ -92,7 +92,7 @@ export const phone = { allow: "Allow", deny: "Deny", sending: "Sending…", - noteLabel: "Note for the bot (optional)", + noteLabel: "Note for the agent (optional)", cut: "Too long to read on a phone. You can deny it here, or answer it at the computer.", atComputer: "This one needs you at the computer. Here you can only deny it.", failed: "Not sent. Check the connection and try again.", @@ -115,7 +115,7 @@ export const phone = { notices: { turnOn: "Turn on notices", turnOff: "Turn off notices", - on: "Notices are on. You get one when a bot needs you.", + on: "Notices are on. You get one when an agent needs you.", off: "Notices are off.", blocked: "Notices are blocked for this page. Allow them in the browser's settings.", unsupported: "This browser cannot give notices.", diff --git a/app/src/i18n/en/questions.ts b/app/src/i18n/en/questions.ts index a682c85f..fe226566 100644 --- a/app/src/i18n/en/questions.ts +++ b/app/src/i18n/en/questions.ts @@ -22,16 +22,16 @@ export const questions = { /** The entry on the left, with how many questions wait. */ open: (count: number) => count === 0 - ? "Questions from your bots" + ? "Questions from your agents" : count === 1 ? "1 question waits for you" : `${count} questions wait for you`, - title: "Questions from your bots", + title: "Questions from your agents", intro: - "When a bot needs your decision to go on, it asks here and carries on. Your answer goes to it as a message.", + "When an agent needs your decision to go on, it asks here and carries on. Your answer goes to it as a message.", emptyTitle: "No question is waiting for you", emptyBody: - "Bots ask here when they need you, also at night, in a routine. Answer whenever you can.", + "Agents ask here when they need you, also at night, in a routine. Answer whenever you can.", openChat: (bot: string) => `Open the chat with ${bot}`, inCrew: (crew: string) => `in ${crew}`, }, diff --git a/app/src/i18n/en/routines.ts b/app/src/i18n/en/routines.ts index 9a67224c..9b1ba7ff 100644 --- a/app/src/i18n/en/routines.ts +++ b/app/src/i18n/en/routines.ts @@ -6,7 +6,7 @@ export const routines = { empty: (bot: string) => `${bot} has no routines yet.`, emptyHint: (bot: string) => `A routine makes ${bot} work on its own at set times, like "weekdays at 09:00", and the result shows up in the chat.`, - crewEmpty: "No bot in this crew has a routine yet. Open a bot to give it one.", + crewEmpty: "No agent in this crew has a routine yet. Open an agent to give it one.", newRoutine: "New routine", toggle: (name: string) => `${name} on or off`, runNow: "Run now", @@ -24,7 +24,7 @@ export const routines = { done: "Last run went fine", failed: "Last run failed", overlap: "Last time was skipped: the run before hadn't finished", - bot_paused: "Last time was skipped: the bot was paused", + bot_paused: "Last time was skipped: the agent was paused", too_soon: "Last signal was skipped: it came less than 5 minutes after the one before", signalFrom: (bot: string) => `signal from ${bot}`, missed: (count: number) => @@ -35,7 +35,7 @@ export const routines = { /** Every routine of every bot, a page of the sidebar. */ page: { title: "Routines", - lead: "What your bots do on their own, in every crew.", + lead: "What your agents do on their own, in every crew.", upcoming: "Coming up", watching: "Keeping watch", showMore: (count: number) => `Show ${count} more`, @@ -43,7 +43,7 @@ export const routines = { of: (bot: string) => `${bot}'s routines`, emptyTitle: "No routines yet", emptyBody: - "A routine makes a bot work on its own at set times. Ask a bot in the chat, or open a bot's Routines tab.", + "A routine makes an agent work on its own at set times. Ask an agent in the chat, or open an agent's Routines tab.", }, when: { everyDay: (time: string) => `Every day at ${time}`, @@ -53,7 +53,7 @@ export const routines = { everyMinutes: (count: number) => (count === 1 ? "Every minute" : `Every ${count} minutes`), everyHours: (count: number) => (count === 1 ? "Every hour" : `Every ${count} hours`), cron: (expr: string) => `Cron: ${expr}`, - signal: (name: string) => `When a bot signals “${name}”`, + signal: (name: string) => `When an agent signals “${name}”`, }, tag: (name: string) => `Routine · ${name}`, dialog: { @@ -69,7 +69,7 @@ export const routines = { weekdays: "Weekdays", days: "Chosen days", interval: "Every…", - signal: "When a bot signals", + signal: "When an agent signals", }, at: "At", every: "Every", @@ -89,7 +89,7 @@ export const routines = { signal: "Signal", signalPlaceholder: "report-ready", signalHint: (bot: string) => - `${bot} runs this when a bot of the crew sends this signal. Tell that bot, in its instructions, when to send it, like “send report-ready when the report is saved”.`, + `${bot} runs this when an agent of the crew sends this signal. Tell that agent, in its instructions, when to send it, like “send report-ready when the report is saved”.`, cronHint: "Minute, hour, day of the month, month and day of the week. It replaces When.", create: "Create routine", save: "Save", @@ -108,12 +108,12 @@ export const routines = { /** The daily summary the chief writes (spec 29.2), a routine made for the owner. */ summary: { title: "Daily summary", - hint: "Every day the chief tells you what each bot did, what is stuck and what is waiting for you.", + hint: "Every day the chief tells you what each agent did, what is stuck and what is waiting for you.", name: "Daily summary", // Sent to the chief every day. It names the tool, which is also how the app // recognizes this routine. prompt: - "The day is ending. Use crew_activity (the last 24 hours) and write me a short summary of the day: what each bot finished, what is still open or late, what failed, and what is waiting for me (approvals and questions), the most important first. Plain words, no more than 12 lines. If nothing happened, say so in one line. Do not start new work and do not message other bots for this.", + "The day is ending. Use crew_activity (the last 24 hours) and write me a short summary of the day: what each agent finished, what is still open or late, what failed, and what is waiting for me (approvals and questions), the most important first. Plain words, no more than 12 lines. If nothing happened, say so in one line. Do not start new work and do not message other agents for this.", time: "Time", turnOn: "Turn on", turnOff: "Turn off", diff --git a/app/src/i18n/en/search.ts b/app/src/i18n/en/search.ts index 904147d3..0454f88a 100644 --- a/app/src/i18n/en/search.ts +++ b/app/src/i18n/en/search.ts @@ -9,7 +9,7 @@ export const search = { placeholder: "Find words in your chats", scope: "Where to search", allCrews: "All crews", - hint: "Finds words in what you, other bots and Botloft wrote to your bots, in what they replied and in their questions.", + hint: "Finds words in what you, other agents and Botloft wrote to your agents, in what they replied and in their questions.", tooShort: "Type at least 2 letters", results: "Results", nothing: (query: string) => `Nothing found for “${query}”`, @@ -20,6 +20,6 @@ export const search = { /** Who wrote what was found. */ you: "You", botloft: "Botloft", - goneBot: "A bot no longer in the crew", + goneBot: "An agent no longer in the crew", openHere: (bot: string) => `Open the chat with ${bot} here`, }; diff --git a/app/src/i18n/en/setup.ts b/app/src/i18n/en/setup.ts index c9ed7fb0..6021d530 100644 --- a/app/src/i18n/en/setup.ts +++ b/app/src/i18n/en/setup.ts @@ -1,7 +1,7 @@ // The window people install Botloft with (spec 15.7). export const setup = { - tagline: "Always-on bots that work together for you.", + tagline: "Always-on agents that work together for you.", install: "Install", update: "Update", forYou: "Installs just for you, without asking for an administrator.", @@ -10,7 +10,7 @@ export const setup = { newer: (installed: string) => `You already have a newer version (${installed}).`, open: "Open Botloft", reinstall: "Install again", - appOpen: "Botloft is open and closes to install. Your bots keep working.", + appOpen: "Botloft is open and closes to install. Your agents keep working.", installing: "Installing Botloft…", installingLine: "This takes a few seconds.", progress: "Installing", diff --git a/app/src/i18n/en/shell.ts b/app/src/i18n/en/shell.ts index 54ccb310..a4fc7feb 100644 --- a/app/src/i18n/en/shell.ts +++ b/app/src/i18n/en/shell.ts @@ -15,8 +15,8 @@ export const shell = { home: "Home", }, sidebar: { - hide: "Hide the bots list", - show: "Show the bots list", + hide: "Hide the agents list", + show: "Show the agents list", }, zoom: { level: (percent: number, isDefault: boolean) => @@ -35,9 +35,9 @@ export const shell = { /** A deleted bot's or crew's folder that could not go to the Recycle Bin. */ recycleFailed: (path: string) => `The folder ${path} did not go to the Recycle Bin and is still there`, - botsCantStart: "Bots can't start", + botsCantStart: "Agents can't start", signIn: { title: "Sign in to Claude", - body: "Your bots work with your Claude account. Sign in once and they start by themselves.", + body: "Your agents work with your Claude account. Sign in once and they start by themselves.", }, }; diff --git a/app/src/i18n/en/tools.ts b/app/src/i18n/en/tools.ts index 7a8bc9af..e5c6847d 100644 --- a/app/src/i18n/en/tools.ts +++ b/app/src/i18n/en/tools.ts @@ -31,12 +31,12 @@ export const tools = { send_message: "Send a message", complete_task: "Finish a task", my_tasks: "See its tasks", - suggest_bot: "Suggest a bot", + suggest_bot: "Suggest an agent", schedule_routine: "Set up a routine", my_routines: "See my routines", change_routine: "Change a routine", delete_routine: "Delete a routine", - change_bot: "Change a bot", + change_bot: "Change an agent", ask_crew_access: "Reach another crew", share_file: "Share a file", ask_owner: "Ask you a question", diff --git a/app/src/i18n/en/updates.ts b/app/src/i18n/en/updates.ts index e3f4adf4..163895b2 100644 --- a/app/src/i18n/en/updates.ts +++ b/app/src/i18n/en/updates.ts @@ -4,7 +4,7 @@ export const updates = { available: "Update available", title: "Update Botloft", ready: (version: string) => - `Botloft ${version} is ready. Botloft closes, installs the update and opens again. Your bots pause for a moment and pick up where they left off.`, + `Botloft ${version} is ready. Botloft closes, installs the update and opens again. Your agents pause for a moment and pick up where they left off.`, whatsNew: "What's new", later: "Later", updateNow: "Update now", diff --git a/app/src/i18n/es/account.ts b/app/src/i18n/es/account.ts index 28f6e04c..ab87667c 100644 --- a/app/src/i18n/es/account.ts +++ b/app/src/i18n/es/account.ts @@ -15,8 +15,8 @@ export const account: Messages["account"] = { usage: { title: "Uso", intro: - "Cuánto de tu plan de Claude han usado tus bots. Se actualiza cada vez que un bot trabaja.", - empty: "El uso aparece después de la primera respuesta de un bot.", + "Cuánto de tu plan de Claude han usado tus agentes. Se actualiza cada vez que un agente trabaja.", + empty: "El uso aparece después de la primera respuesta de un agente.", windows: { five_hour: "Ventana de 5 horas", seven_day: "Esta semana", @@ -25,12 +25,12 @@ export const account: Messages["account"] = { }, used: (percent: number) => `${percent}% usado`, resets: (relative: string) => `Se renueva ${relative}`, - limited: "Límite alcanzado. Tus bots esperan hasta que se renueve.", + limited: "Límite alcanzado. Tus agentes esperan hasta que se renueve.", updated: (relative: string) => `Actualizado ${relative}`, tokens: { - title: "Uso por bot", + title: "Uso por agente", intro: - "Cuánto de tu plan semanal usó cada bot, y los tokens detrás: los trozos de texto que un bot lee y escribe.", + "Cuánto de tu plan semanal usó cada agente, y los tokens detrás: los trozos de texto que un agente lee y escribe.", period: "Período", periods: { hour: "Última hora", @@ -39,25 +39,25 @@ export const account: Messages["account"] = { month: "30 días", all: "Todo", }, - empty: "Ningún bot trabajó en este período.", + empty: "Ningún agente trabajó en este período.", failed: "No se pudieron cargar los tokens", archived: "archivado", /** `crew` is null on the total of all bots. */ detail: (crew: string | null, times: number) => `${crew === null ? "" : `${crew} · `}Trabajó ${times === 1 ? "1 vez" : `${times} veces`}`, - total: "Todos los bots", + total: "Todos los agentes", share: (percent) => `≈ ${percent} de la semana`, tokensUsed: (count) => `${count} tokens`, estimate: - "≈ Estimado: Botloft aprende cuánto de tu plan consume el trabajo de los bots según cuánto sube el plan semanal mientras trabajan. Lo que usas fuera de Botloft también lo sube, así que los bots pueden aparecer con un poco más de lo que usaron.", + "≈ Estimado: Botloft aprende cuánto de tu plan consume el trabajo de los agentes según cuánto sube el plan semanal mientras trabajan. Lo que usas fuera de Botloft también lo sube, así que los agentes pueden aparecer con un poco más de lo que usaron.", learning: - "La parte de cada bot en tu plan semanal aparece cuando el plan haya subido unos puntos con los bots trabajando. Hasta entonces, los tokens.", + "La parte de cada agente en tu plan semanal aparece cuando el plan haya subido unos puntos con los agentes trabajando. Hasta entonces, los tokens.", }, }, backup: { exportTitle: "Guardar una copia", exportIntro: - "Guarda tus equipos, bots, conversaciones y la memoria de cada bot en un archivo cerrado con una contraseña, para recuperarlo tras reinstalar o en otra computadora. Las carpetas de trabajo que elegiste fuera de Botloft no entran.", + "Guarda tus equipos, agentes, conversaciones y la memoria de cada agente en un archivo cerrado con una contraseña, para recuperarlo tras reinstalar o en otra computadora. Las carpetas de trabajo que elegiste fuera de Botloft no entran.", passphrase: "Contraseña", passphraseHint: "Al menos 8 caracteres. Sin ella la copia no se abre, y Botloft no la guarda en ningún lado.", @@ -79,7 +79,7 @@ export const account: Messages["account"] = { opening: "Abriendo…", from: (when) => `Copia del ${when}`, crew: (name, bots) => - `${name}: ${bots === 0 ? "ningún bot" : bots === 1 ? "1 bot" : `${bots} bots`}`, + `${name}: ${bots === 0 ? "ningún agente" : bots === 1 ? "1 agente" : `${bots} agentes`}`, workFolders: "Estas carpetas de trabajo no están en la copia. Si es otra computadora, vuelve a poner sus archivos:", replaces: "Se cambia todo en este Botloft. Botloft se reinicia para hacerlo.", @@ -89,7 +89,7 @@ export const account: Messages["account"] = { openFailed: "No se pudo abrir la copia", restoreFailed: "No se pudo restaurar la copia", lightNote: - "Esta copia tiene los equipos, los bots, su memoria y las rutinas. Las conversaciones y los archivos no están en ella.", + "Esta copia tiene los equipos, los agentes, su memoria y las rutinas. Las conversaciones y los archivos no están en ella.", reasons: { short_passphrase: "La contraseña necesita al menos 8 caracteres.", wrong_passphrase: "La contraseña es incorrecta, o el archivo está dañado.", @@ -117,7 +117,7 @@ export const account: Messages["account"] = { usage: (used: string, quota: string) => `${used} de ${quota} usados`, uploadTitle: "Enviar una copia a la nube", uploadIntro: - "Suben: tus equipos, los bots, la memoria de cada uno y las rutinas. Las conversaciones y los archivos se quedan solo en este ordenador. La copia se cierra con una contraseña que nunca sale de él.", + "Suben: tus equipos, los agentes, la memoria de cada uno y las rutinas. Las conversaciones y los archivos se quedan solo en este ordenador. La copia se cierra con una contraseña que nunca sale de él.", upload: "Enviar una copia ahora", uploading: "Enviando…", uploaded: (size: string) => `Copia enviada (${size}).`, @@ -180,10 +180,10 @@ export const account: Messages["account"] = { mobile: { title: "Móvil", intro: - "Aprueba las solicitudes de tus bots, responde a sus preguntas y habla con ellos desde cualquier lugar. Los archivos se quedan solo en este ordenador.", + "Aprueba las solicitudes de tus agentes, responde a sus preguntas y habla con ellos desde cualquier lugar. Los archivos se quedan solo en este ordenador.", needAccount: "Inicia sesión en la cuenta de arriba para usar el móvil.", warning: - "Quien tenga un móvil conectado y desbloqueado puede leer las conversaciones de tus bots, escribirles y permitir solicitudes hasta que lo desconectes. Un PIN en el móvil ayuda; lo activas allí.", + "Quien tenga un móvil conectado y desbloqueado puede leer las conversaciones de tus agentes, escribirles y permitir solicitudes hasta que lo desconectes. Un PIN en el móvil ayuda; lo activas allí.", connect: "Conectar un móvil", starting: "Preparando…", qrLabel: "Código para escanear con el móvil", @@ -212,55 +212,58 @@ export const account: Messages["account"] = { title: "Configuración", background: "En segundo plano", keepWorking: "Seguir trabajando después de cerrar Botloft", - keepWorkingOn: "Tus bots siguen trabajando y respondiendo después de que cierras esta ventana.", + keepWorkingOn: + "Tus agentes siguen trabajando y respondiendo después de que cierras esta ventana.", keepWorkingOff: - "Al cerrar Botloft, todos los bots se detienen. Siguen donde se quedaron cuando lo vuelves a abrir.", + "Al cerrar Botloft, todos los agentes se detienen. Siguen donde se quedaron cuando lo vuelves a abrir.", tray: "Mostrar Botloft junto al reloj", trayOn: - "Con la ventana cerrada, su icono queda junto al reloj: un clic abre Botloft, y te avisa cuando un bot te necesita.", - trayOff: "Cerrar la ventana cierra Botloft. Los bots siguen trabajando, sin icono ni avisos.", + "Con la ventana cerrada, su icono queda junto al reloj: un clic abre Botloft, y te avisa cuando un agente te necesita.", + trayOff: + "Cerrar la ventana cierra Botloft. Los agentes siguen trabajando, sin icono ni avisos.", startWithSystem: (system: string) => `Iniciar con ${system}`, startWithSystemOn: (system: string) => - `Cuando inicias sesión en ${system}, tus bots vuelven a trabajar solos, sin abrir esta ventana.`, - startWithSystemOff: "Después de reiniciar el equipo, los bots esperan a que abras Botloft.", + `Cuando inicias sesión en ${system}, tus agentes vuelven a trabajar solos, sin abrir esta ventana.`, + startWithSystemOff: "Después de reiniciar el equipo, los agentes esperan a que abras Botloft.", openAtSignIn: (system: string) => `Abrir la ventana al iniciar sesión en ${system}`, openAtSignInOn: (system: string) => `La ventana de Botloft se abre cuando inicias sesión en ${system}.`, openAtSignInNearClock: "Botloft empieza junto al reloj, sin abrir la ventana.", openAtSignInOff: "La ventana solo se abre cuando abres Botloft.", alerts: "Avisos", - notifyNeeds: "Cuando un bot te necesite", + notifyNeeds: "Cuando un agente te necesite", notifyNeedsHint: (system: string) => - `Un aviso de ${system} cuando un bot pide permiso o necesita que inicies sesión, si Botloft no está al frente.`, - notifyDone: "Cuando un bot termine", - notifyDoneHint: "Un aviso cuando un bot termina lo que estaba haciendo.", - markReplies: "Marcar el icono cuando un bot responda", + `Un aviso de ${system} cuando un agente pide permiso o necesita que inicies sesión, si Botloft no está al frente.`, + notifyDone: "Cuando un agente termine", + notifyDoneHint: "Un aviso cuando un agente termina lo que estaba haciendo.", + markReplies: "Marcar el icono cuando un agente responda", markRepliesHint: "Un punto en el icono de Botloft en la barra de tareas mientras haya una conversación sin leer.", sound: "Reproducir un sonido", soundHint: "Un sonido corto con cada aviso, también con Botloft al frente.", appSounds: "Sonidos de la app", appSoundsHint: - "Sonidos suaves con Botloft al frente: mensaje enviado, respuesta o archivo en el chat abierto, bot o equipo nuevo.", + "Sonidos suaves con Botloft al frente: mensaje enviado, respuesta o archivo en el chat abierto, agente o equipo nuevo.", alertsNeedTray: "Con la ventana cerrada, los avisos solo llegan con Botloft junto al reloj (en General).", - keepAwake: "Mantener el equipo despierto mientras los bots trabajan", + keepAwake: "Mantener el equipo despierto mientras los agentes trabajan", keepAwakeHint: "Igual se suspende si cierras la tapa o eliges Suspender.", saveFailed: "No se pudo cambiar el ajuste", pages: "Partes de la configuración", general: "General", - defaultAgent: "Agente de los bots nuevos", + defaultAgent: "Los agentes nuevos funcionan con", defaultAgentHint: - "Vale para los bots nuevos, el jefe de un equipo nuevo y los bots que añade una plantilla. Cada bot conserva el agente con el que se creó.", + "Vale para los agentes nuevos, el jefe de un equipo nuevo y los agentes que añade una plantilla. Cada agente conserva el motor con el que se creó.", chat: "Chat", enterSends: "Enter envía el mensaje", enterSendsOn: "Shift+Enter empieza una nueva línea.", enterSendsOff: "Enter empieza una nueva línea, y Ctrl+Enter envía.", - followBot: "Abrir el navegador y las pantallas cuando un bot empiece a usarlos", - followBotOn: "El panel se abre junto al chat, para que veas lo que hace el bot.", + followBot: "Abrir el navegador y las pantallas cuando un agente empiece a usarlos", + followBotOn: "El panel se abre junto al chat, para que veas lo que hace el agente.", followBotOff: "El botón del panel muestra un punto, y lo abres cuando quieras.", approvalWait: "Cuánto espera tu respuesta un pedido de permiso", - approvalWaitHint: "Sin respuesta en ese tiempo, el pedido del bot se deniega y sigue sin eso.", + approvalWaitHint: + "Sin respuesta en ese tiempo, el pedido del agente se deniega y sigue sin eso.", waitFor: (minutes: number) => minutes < 60 || minutes % 60 !== 0 ? `${minutes} min` @@ -278,12 +281,12 @@ export const account: Messages["account"] = { language: "Idioma", archived: "Archivados", archivedIntro: - "Los bots y los equipos que archivaste están detenidos y fuera de la vista, y Botloft todavía guarda sus conversaciones. Elimina uno para quitarlo para siempre.", + "Los agentes y los equipos que archivaste están detenidos y fuera de la vista, y Botloft todavía guarda sus conversaciones. Elimina uno para quitarlo para siempre.", archivedEmpty: "No hay nada archivado.", archivedLoadFailed: "No se pudo cargar lo archivado", archivedCrew: (bots: number, when: string) => - `Equipo · ${bots === 1 ? "1 bot" : `${bots} bots`} · archivado ${when}`, - archivedBot: (crew: string, when: string) => `Bot de ${crew} · archivado ${when}`, + `Equipo · ${bots === 1 ? "1 agente" : `${bots} agentes`} · archivado ${when}`, + archivedBot: (crew: string, when: string) => `Agente de ${crew} · archivado ${when}`, deleteArchived: "Eliminar", deleteArchivedOne: (name: string) => `Eliminar ${name}`, about: "Acerca de", diff --git a/app/src/i18n/es/alerts.ts b/app/src/i18n/es/alerts.ts index 31117142..33744c25 100644 --- a/app/src/i18n/es/alerts.ts +++ b/app/src/i18n/es/alerts.ts @@ -5,10 +5,10 @@ import type { Messages } from "../en"; export const alerts: Messages["alerts"] = { tray: { tooltip: (status) => `Botloft: ${status}`, - working: (count) => (count === 1 ? "1 bot trabajando" : `${count} bots trabajando`), + working: (count) => (count === 1 ? "1 agente trabajando" : `${count} agentes trabajando`), needsYou: (bot) => `${bot} te necesita`, waiting: "Algo te está esperando", - idle: "Ningún bot trabajando", + idle: "Ningún agente trabajando", open: "Abrir Botloft", pause: "Pausar todos los equipos", resume: "Reanudar todos los equipos", diff --git a/app/src/i18n/es/bots.ts b/app/src/i18n/es/bots.ts index e2c8d894..759fa35c 100644 --- a/app/src/i18n/es/bots.ts +++ b/app/src/i18n/es/bots.ts @@ -16,7 +16,7 @@ export const bots: Messages["bots"] = { }, auth_error: { label: "Necesita iniciar sesión", - hint: "Claude Code no tiene una sesión iniciada, o la cuenta no se puede usar. Inicia sesión en Claude y el bot vuelve a iniciarse solo. ¿Ya iniciaste sesión? Revisa tu plan de Claude y luego reinicia el bot.", + hint: "Claude Code no tiene una sesión iniciada, o la cuenta no se puede usar. Inicia sesión en Claude y el agente vuelve a iniciarse solo. ¿Ya iniciaste sesión? Revisa tu plan de Claude y luego reinicia el agente.", }, backoff: { label: "Reiniciando", @@ -27,7 +27,7 @@ export const bots: Messages["bots"] = { }, chief: { badge: "Jefe", - hint: (crew: string) => `Dirige ${crew}: planea el trabajo y sugiere bots nuevos`, + hint: (crew: string) => `Dirige ${crew}: planea el trabajo y sugiere agentes nuevos`, }, header: { noRole: "Sin rol", @@ -36,7 +36,7 @@ export const bots: Messages["bots"] = { restart: "Reiniciar", showDetails: "Mostrar detalles", hideDetails: "Ocultar detalles", - more: "Más acciones del bot", + more: "Más acciones del agente", menuOf: (name: string) => `Acciones de ${name}`, edit: "Editar", restartFresh: "Reiniciar con una conversación nueva", @@ -45,16 +45,16 @@ export const bots: Messages["bots"] = { stopChief: "Dejar de ser jefe", markUnread: "Marcar como no leído", markRead: "Marcar como leído", - archive: "Archivar bot", - delete: "Eliminar bot", + archive: "Archivar agente", + delete: "Eliminar agente", failed: { - pause: "No se pudo pausar el bot", - resume: "No se pudo reanudar el bot", - restart: "No se pudo reiniciar el bot", + pause: "No se pudo pausar el agente", + resume: "No se pudo reanudar el agente", + restart: "No se pudo reiniciar el agente", openFolder: "No se pudo abrir la carpeta", chief: "No se pudo cambiar el jefe", - archive: "No se pudo archivar el bot", - delete: "No se pudo eliminar el bot", + archive: "No se pudo archivar el agente", + delete: "No se pudo eliminar el agente", }, fresh: { title: "¿Empezar una conversación nueva?", @@ -64,12 +64,12 @@ export const bots: Messages["bots"] = { }, archiveConfirm: { title: (name: string) => `¿Archivar ${name}?`, - confirm: "Archivar bot", - body: "El bot se detiene y sale del equipo. Los mensajes que aún lo esperan no se entregan.", + confirm: "Archivar agente", + body: "El agente se detiene y sale del equipo. Los mensajes que aún lo esperan no se entregan.", }, deleteConfirm: { title: (name: string) => `¿Eliminar ${name}?`, - confirm: "Eliminar bot", + confirm: "Eliminar agente", removed: (name: string, running: boolean) => `${name} ${running ? "se detiene ahora y sale" : "sale"} de Botloft para siempre, con su conversación, sus rutinas y las tareas en las que participaba. No se puede deshacer.`, chief: (crew: string) => `${crew} se queda sin jefe.`, @@ -81,45 +81,45 @@ export const bots: Messages["bots"] = { }, }, dialog: { - newTitle: "Nuevo bot", + newTitle: "Nuevo agente", editTitle: (name: string) => `Editar ${name}`, - create: "Crear bot", + create: "Crear agente", save: "Guardar", name: "Nombre", namePlaceholder: "Revisor", - nameHint: "Los otros bots lo llaman por el @ que se forma con este nombre.", + nameHint: "Los otros agentes lo llaman por el @ que se forma con este nombre.", role: "Rol", rolePlaceholder: "Revisa los pull requests antes de fusionarlos", instructions: "Instrucciones", instructionsPlaceholder: - "Cómo trabaja este bot, qué puede hacer por su cuenta y cuándo debe preguntar.", + "Cómo trabaja este agente, qué puede hacer por su cuenta y cuándo debe preguntar.", instructionsHint: - "Se guardan ahora en las reglas del bot; el bot las lee la próxima vez que se inicie.", + "Se guardan ahora en las reglas del agente; el agente las lee la próxima vez que se inicie.", fullAccess: { title: "Ejecutar cualquier comando sin preguntar", off: "Desactivado: solo ejecuta los comandos de la lista de abajo.", - on: "Activado: ejecuta cualquier comando sin preguntarte, y un comando puede leer o cambiar todo lo que tú puedes. Solo para un bot de confianza.", + on: "Activado: ejecuta cualquier comando sin preguntarte, y un comando puede leer o cambiar todo lo que tú puedes. Solo para un agente de confianza.", }, commands: { - title: "Comandos que este bot puede ejecutar", + title: "Comandos que este agente puede ejecutar", placeholder: "git status\nnpm test", - hint: "Un comando por línea; puede ejecutar estos y lo que empiece con ellos, sin preguntarte. No puede preguntar, así que todo lo demás se rechaza. Un * solo permite ejecutar cualquier cosa. El bot se reinicia para usar la lista.", + hint: "Un comando por línea; puede ejecutar estos y lo que empiece con ellos, sin preguntarte. No puede preguntar, así que todo lo demás se rechaza. Un * solo permite ejecutar cualquier cosa. El agente se reinicia para usar la lista.", }, agent: { - title: "Agente", + title: "Funciona con", names: { claude: "Claude Code", agy: "Antigravity (experimental)", codex: "Codex", }, experimental: - "Experimental. Este bot no puede preguntarte antes de actuar. Trabaja con archivos de sus propias carpetas y usa las herramientas del equipo, pero no ejecuta comandos, y no tiene medidor de uso ni elección de modelo.", + "Experimental. Este agente no puede preguntarte antes de actuar. Trabaja con archivos de sus propias carpetas y usa las herramientas del equipo, pero no ejecuta comandos, y no tiene medidor de uso ni elección de modelo.", experimentalCodex: - "Experimental. Este bot te pregunta antes de cambiar un archivo o ejecutar un comando, como Claude Code. Todavía puede leer cualquier archivo.", + "Experimental. Este agente te pregunta antes de cambiar un archivo o ejecutar un comando, como Claude Code. Todavía puede leer cualquier archivo.", }, color: "Color", swatch: (color: string) => `Color ${color}`, - colorUnset: "Si no eliges uno, el bot recibe el siguiente color del equipo.", + colorUnset: "Si no eliges uno, el agente recibe el siguiente color del equipo.", custom: "Elegir cualquier color", picker: { area: "Saturación y brillo", @@ -132,7 +132,7 @@ export const bots: Messages["bots"] = { }, notices: { crewPaused: (crew: string) => `El equipo ${crew} está en pausa`, - crewPausedBody: "Sus bots siguen detenidos hasta que reanudes el equipo.", + crewPausedBody: "Sus agentes siguen detenidos hasta que reanudes el equipo.", }, details: { title: (name: string) => `Acerca de ${name}`, diff --git a/app/src/i18n/es/browser.ts b/app/src/i18n/es/browser.ts index 74a70059..9e57f12e 100644 --- a/app/src/i18n/es/browser.ts +++ b/app/src/i18n/es/browser.ts @@ -32,7 +32,7 @@ export const browser: Messages["browser"] = { "Cuando busque o use un sitio, lo verás aquí en vivo: cada página que abre, cada clic.", starting: "Abriendo el navegador…", closed: "Navegador cerrado", - closedBody: "Se abre de nuevo cuando el bot lo necesite. Las sesiones iniciadas siguen.", + closedBody: "Se abre de nuevo cuando el agente lo necesite. Las sesiones iniciadas siguen.", openForMe: "Abrirlo para mí", openForMeWhy: (bot) => `Para ver lo que ${bot} dejó abierto y usarlo tú mismo. ${bot} no se despierta ni se entera; devuélvelo cuando termines y el navegador vuelve a descansar.`, @@ -40,8 +40,8 @@ export const browser: Messages["browser"] = { failedTitle: "El navegador no se abrió", failedBody: (system: string) => system === "Windows" - ? "Botloft usa Microsoft Edge, que viene con Windows. Comprueba que esté instalado y pide al bot que lo intente de nuevo." - : "Botloft usa Google Chrome, Chromium o Microsoft Edge. Comprueba que uno de ellos esté instalado y pide al bot que lo intente de nuevo.", + ? "Botloft usa Microsoft Edge, que viene con Windows. Comprueba que esté instalado y pide al agente que lo intente de nuevo." + : "Botloft usa Google Chrome, Chromium o Microsoft Edge. Comprueba que uno de ellos esté instalado y pide al agente que lo intente de nuevo.", details: "Detalles", tabs: { label: "Pestañas", @@ -126,7 +126,7 @@ export const browser: Messages["browser"] = { help: { needs: (bot) => `${bot} te necesita en el navegador`, asks: (bot, task) => `${bot} pide: ${task}`, - why: "Toma el control, hazlo en la página y devuélvelo. Si el sitio no te deja iniciar sesión ahí, usa Iniciar sesión en una ventana en el panel del navegador. El bot no ve lo que escribes en campos de contraseña.", + why: "Toma el control, hazlo en la página y devuélvelo. Si el sitio no te deja iniciar sesión ahí, usa Iniciar sesión en una ventana en el panel del navegador. El agente no ve lo que escribes en campos de contraseña.", take: "Tomar el navegador", done: "Listo", wontDo: "No lo haré", diff --git a/app/src/i18n/es/catalog.ts b/app/src/i18n/es/catalog.ts index b0a0734f..e4b2da4d 100644 --- a/app/src/i18n/es/catalog.ts +++ b/app/src/i18n/es/catalog.ts @@ -16,17 +16,17 @@ import { sectors } from "./catalogSectors"; import { security } from "./catalogSecurity"; export const catalog: Messages["catalog"] = { - title: "Agencia de bots", - open: "Agencia de bots", - openHint: "Bots listos que puedes añadir a este equipo", + title: "Agencia de agentes", + open: "Agencia de agentes", + openHint: "Agentes listos que puedes añadir a este equipo", intro: - "Elige un bot para el trabajo. Entra en este equipo listo para trabajar, y después puedes cambiar lo que quieras de él.", + "Elige un agente para el trabajo. Entra en este equipo listo para trabajar, y después puedes cambiar lo que quieras de él.", invite: { title: "¿A quién quieres en tu equipo?", - body: "¿No sabes qué crear? Elige un bot para el trabajo. Después puedes cambiar lo que quieras de él.", + body: "¿No sabes qué crear? Elige un agente para el trabajo. Después puedes cambiar lo que quieras de él.", }, - search: "Buscar bots", - filter: "Tipos de bot", + search: "Buscar agentes", + filter: "Tipos de agente", categories: { all: "Todos", code: "Código", @@ -44,23 +44,23 @@ export const catalog: Messages["catalog"] = { games: "Juegos", legal: "Jurídico", }, - none: "Ningún bot coincide con eso.", - loading: "Cargando los bots…", + none: "Ningún agente coincide con eso.", + loading: "Cargando los agentes…", add: "Añadir al equipo", learnMore: "Saber más", - back: "Volver a todos los bots", - seeAll: "Ver todos los bots", + back: "Volver a todos los agentes", + seeAll: "Ver todos los agentes", joined: (name) => `${name} entró en el equipo`, customize: "Personalizar", failed: { - load: "No se pudieron cargar los bots", - add: "No se pudo añadir el bot", + load: "No se pudieron cargar los agentes", + add: "No se pudo añadir el agente", }, detail: { what: "Qué hace", when: "Cuándo llamarlo", pairs: "Combina bien con", - technical: "Lo que recibe el bot cuando empieza", + technical: "Lo que recibe el agente cuando empieza", }, roles: { ...base, diff --git a/app/src/i18n/es/catalogBase.ts b/app/src/i18n/es/catalogBase.ts index a01f1110..a2701c93 100644 --- a/app/src/i18n/es/catalogBase.ts +++ b/app/src/i18n/es/catalogBase.ts @@ -13,10 +13,10 @@ export const base: typeof en = { }, "code-reviewer": { name: "Revisor de código", - role: "Revisa lo que cambiaron otros bots y señala errores, riesgos y complicaciones innecesarias", + role: "Revisa lo que cambiaron otros agentes y señala errores, riesgos y complicaciones innecesarias", summary: "Lee los cambios con ojo crítico y dice qué está mal o es arriesgado", about: - "Lee los cambios que hizo otro bot y enumera lo que puede romperse, lo que es arriesgado y lo que está más complicado de lo necesario. Nunca edita el código, solo informa, y la decisión es tuya. Piensa más a fondo que la mayoría de los bots, así que gasta un poco más de tu plan.", + "Lee los cambios que hizo otro agente y enumera lo que puede romperse, lo que es arriesgado y lo que está más complicado de lo necesario. Nunca edita el código, solo informa, y la decisión es tuya. Piensa más a fondo que la mayoría de los agentes, así que gasta un poco más de tu plan.", when: "Después de que un Desarrollador cambie algo importante: un pago, un acceso, cualquier cosa difícil de deshacer.", pairs: "Desarrollador, cuyo trabajo revisa, y Tester.", }, diff --git a/app/src/i18n/es/catalogDesign.ts b/app/src/i18n/es/catalogDesign.ts index 1021229b..82c569d7 100644 --- a/app/src/i18n/es/catalogDesign.ts +++ b/app/src/i18n/es/catalogDesign.ts @@ -7,7 +7,7 @@ export const design: typeof en = { summary: "Escribe una guía breve de la marca con tu material y revisa que todo la siga", about: "Reúne tu logo, colores, fuentes y ejemplos en una guía de una página, marcando lo que vio y lo que sugiere. Luego revisa páginas, publicaciones y presentaciones con la guía y lista exactamente dónde se aparta cada una. Nunca inventa un dato de la marca ni cambia una regla sin preguntarte.", - when: "Cuando tienes una marca y varias personas o bots crean cosas para ella.", + when: "Cuando tienes una marca y varias personas o agentes crean cosas para ella.", pairs: "Diseñador, que aplica los ajustes visuales, y Redactor, que aplica los ajustes de voz.", }, "ui-designer": { diff --git a/app/src/i18n/es/catalogEngineering.ts b/app/src/i18n/es/catalogEngineering.ts index da76d421..e0424082 100644 --- a/app/src/i18n/es/catalogEngineering.ts +++ b/app/src/i18n/es/catalogEngineering.ts @@ -66,7 +66,7 @@ export const engineering: typeof en = { role: "Revisa tu propio código y tu configuración en busca de fallos de seguridad y explica cómo corregirlos", summary: "Busca fallos de seguridad en tu código y tu configuración y explica cómo corregirlos", about: - "Revisa tu código y tu configuración en busca de fallos, explica cada uno con palabras sencillas, su gravedad y la corrección más pequeña. Trabaja a la defensiva, solo sobre lo que es tuyo, y nunca repite un secreto que encuentre. Piensa más a fondo que la mayoría de los bots, así que gasta un poco más de tu plan.", + "Revisa tu código y tu configuración en busca de fallos, explica cada uno con palabras sencillas, su gravedad y la corrección más pequeña. Trabaja a la defensiva, solo sobre lo que es tuyo, y nunca repite un secreto que encuentre. Piensa más a fondo que la mayoría de los agentes, así que gasta un poco más de tu plan.", when: "Antes de lanzar, después de un cambio grande, o cuando manejas datos de otras personas.", pairs: "Desarrollador e Ingeniero DevOps, que aplican las correcciones, y Revisor de código.", }, diff --git a/app/src/i18n/es/catalogProduct.ts b/app/src/i18n/es/catalogProduct.ts index 97f16c89..55a3e35c 100644 --- a/app/src/i18n/es/catalogProduct.ts +++ b/app/src/i18n/es/catalogProduct.ts @@ -47,8 +47,8 @@ export const product: typeof en = { summary: "Mantiene el trabajo del equipo en marcha con pasos pequeños, seguimiento regular y retrospectivas honestas", about: - "Mantiene un tablero simple de lo que viene, lo que está en curso y lo que está hecho, pide actualizaciones rápidas a los bots y dirige una retrospectiva al final de cada ronda para cambiar una cosa.", - when: "Cuando el equipo tiene muchos bots y el trabajo se atasca o se olvida.", + "Mantiene un tablero simple de lo que viene, lo que está en curso y lo que está hecho, pide actualizaciones rápidas a los agentes y dirige una retrospectiva al final de cada ronda para cambiar una cosa.", + when: "Cuando el equipo tiene muchos agentes y el trabajo se atasca o se olvida.", pairs: "Gerente de proyectos, para el plan, y Secretario de reuniones.", }, "goals-coach": { diff --git a/app/src/i18n/es/chat.ts b/app/src/i18n/es/chat.ts index 1b800946..ec6d80d3 100644 --- a/app/src/i18n/es/chat.ts +++ b/app/src/i18n/es/chat.ts @@ -39,13 +39,13 @@ export const chat: Messages["chat"] = { showInFolder: (file: string) => `Mostrar ${file} en su carpeta`, showInFolderHint: "Mostrar en la carpeta", openFolderFailed: "No se pudo abrir la carpeta", - missing: "Ya no está en la carpeta del bot", + missing: "Ya no está en la carpeta del agente", loading: (file: string) => `Cargando ${file}`, }, inbound: { task: "Tarea", result: "Resultado", - goneBot: "Un bot que ya no está en el equipo", + goneBot: "Un agente que ya no está en el equipo", routine: "Mostrar lo que pide la rutina", }, run: { @@ -56,7 +56,7 @@ export const chat: Messages["chat"] = { `Tardó ${time}${ tokens === null ? "" : `. Leyó ${tokens.read} tokens y escribió ${tokens.wrote}.` }`, - stopped: (reason: string) => `El bot dejó de trabajar en esto: ${reason}`, + stopped: (reason: string) => `El agente dejó de trabajar en esto: ${reason}`, }, tools: { label: "Llamadas a herramientas", @@ -112,7 +112,7 @@ export const chat: Messages["chat"] = { `${bot} no dijo para qué sirve este comando. Si tienes dudas, deniégalo y pregunta.`, command: "Ver el comando", noteLabel: (bot: string) => `Nota para ${bot} si deniegas`, - notePlaceholder: "¿Por qué no? Se envía al bot si deniegas (opcional)", + notePlaceholder: "¿Por qué no? Se envía al agente si deniegas (opcional)", allow: "Permitir", deny: "Denegar", always: { @@ -154,14 +154,14 @@ export const chat: Messages["chat"] = { bypassBody: (bot: string) => `${bot} editará archivos, ejecutará comandos y usará internet en este equipo sin pedirte permiso antes.`, bypassRisk: - "No se limita a su carpeta: puede leer y cambiar tus otros archivos, los de otros bots y los del propio Botloft. Un mensaje de otra persona podría llevarlo a hacer algo que no querías.", + "No se limita a su carpeta: puede leer y cambiar tus otros archivos, los de otros agentes y los del propio Botloft. Un mensaje de otra persona podría llevarlo a hacer algo que no querías.", bypassChief: (bot: string) => - `${bot} es el jefe del equipo, así que también creará bots nuevos sin preguntar.`, + `${bot} es el jefe del equipo, así que también creará agentes nuevos sin preguntar.`, bypassAdvice: - "Actívalo solo para un bot en el que confíes para todo, y solo mientras lo necesites.", + "Actívalo solo para un agente en el que confíes para todo, y solo mientras lo necesites.", badge: "No pregunta nada", badgeHint: - "Este bot lo hace todo sin preguntar. Cámbialo en el selector de modo, debajo del chat.", + "Este agente lo hace todo sin preguntar. Cámbialo en el selector de modo, debajo del chat.", }, model: { title: "Modelo", @@ -197,7 +197,7 @@ export const chat: Messages["chat"] = { loading: "Cargando los modelos…", listFailed: "No se pudo cargar la lista de modelos", failed: "No se pudo cambiar el modelo", - defaultHint: (bot: string) => `${bot} usa el modelo predeterminado del propio agente`, + defaultHint: (bot: string) => `${bot} usa el modelo predeterminado del propio motor`, later: (bot: string, model: string) => `${bot} cambia a ${model}.`, }, effort: { @@ -246,7 +246,7 @@ export const chat: Messages["chat"] = { noAuto: "No se compacta sola.", byItself: "Codex la compacta solo cuando se acerca a llenarse.", estimate: - "Una estimación: este agente no dice el tamaño de su ventana, así que el tamaño sale de la familia del modelo.", + "Una estimación: este motor no dice el tamaño de su ventana, así que el tamaño sale de la familia del modelo.", compact: "Compactar ahora", compactHint: (bot: string) => `Cambia lo anterior por un resumen: ${bot} vuelve a tener espacio y cada mensaje gasta menos.`, @@ -256,7 +256,7 @@ export const chat: Messages["chat"] = { failed: "No se pudo compactar la conversación", }, suggestion: { - title: (bot: string) => `${bot} sugiere un bot nuevo`, + title: (bot: string) => `${bot} sugiere un agente nuevo`, why: "Por qué", name: "Nombre", role: "Rol", @@ -265,9 +265,9 @@ export const chat: Messages["chat"] = { startsNow: (bot: string) => `Empieza enseguida y recibe el trabajo de ${bot}.`, noteLabel: (bot: string) => `Qué decirle a ${bot} si dices que no`, notePlaceholder: (bot: string) => `Si dices que no, dile a ${bot} por qué (opcional)`, - create: "Crear bot", + create: "Crear agente", decline: "Ahora no", - createFailed: "No se pudo crear el bot", + createFailed: "No se pudo crear el agente", declineFailed: "No se pudo enviar la respuesta", created: (name: string) => `Creaste ${name}`, declined: (name: string) => `Rechazaste ${name}`, @@ -329,7 +329,8 @@ export const chat: Messages["chat"] = { read: (whom) => `Leer los archivos de ${whom} y de su carpeta de trabajo`, edit: (whom) => `Cambiar y crear archivos de ${whom} y de su carpeta de trabajo`, why: (bot) => `Por qué, según ${bot}:`, - explain: "Los equipos no se ven entre sí. Solo ahora vale hasta que el bot termine este turno.", + explain: + "Los equipos no se ven entre sí. Solo ahora vale hasta que el agente termine este turno.", once: "Solo ahora", alwaysBot: (other) => `Siempre ${other}`, alwaysCrew: (crew) => `Siempre el equipo ${crew}`, @@ -349,7 +350,7 @@ export const chat: Messages["chat"] = { reason: (bot) => `Por qué, según ${bot}:`, newInstructions: "Ver las instrucciones nuevas", explain: - "El rol, las instrucciones, el modelo y el esfuerzo nuevos cuentan desde el próximo inicio del bot, cuando termine lo que está haciendo.", + "El rol, las instrucciones, el modelo y el esfuerzo nuevos cuentan desde el próximo inicio del agente, cuando termine lo que está haciendo.", apply: "Cambiar", decline: "Ahora no", noteLabel: (bot) => `Qué decirle a ${bot} si dices que no`, @@ -362,7 +363,7 @@ export const chat: Messages["chat"] = { plan: { ready: (bot: string) => `${bot} hizo un plan y quiere seguir con él`, noteLabel: (bot: string) => `Qué debe cambiar ${bot} en el plan`, - notePlaceholder: "¿Qué debe cambiar? Se envía al bot si pides cambios (opcional)", + notePlaceholder: "¿Qué debe cambiar? Se envía al agente si pides cambios (opcional)", approve: "Aprobar plan", keepPlanning: "Pedir cambios", approveFailed: "No se pudo aprobar el plan", @@ -381,8 +382,8 @@ export const chat: Messages["chat"] = { usageLimit: "Tu plan de Claude alcanzó su límite de uso. Los mensajes esperarán hasta que se restablezca.", modelUnavailable: - "El modelo de este bot no está disponible: quizá no esté en tu plan de Claude. Elige otro modelo debajo del chat.", - turnFailed: (detail: string) => `El bot no pudo terminar esto: ${detail}`, + "El modelo de este agente no está disponible: quizá no esté en tu plan de Claude. Elige otro modelo debajo del chat.", + turnFailed: (detail: string) => `El agente no pudo terminar esto: ${detail}`, compacted: "La conversación se compactó: lo anterior ahora es un resumen, y vuelve a haber espacio.", autoCompacted: "La conversación estaba llena y se compactó: lo anterior ahora es un resumen.", diff --git a/app/src/i18n/es/connections.ts b/app/src/i18n/es/connections.ts index 739a461f..8fcda5ac 100644 --- a/app/src/i18n/es/connections.ts +++ b/app/src/i18n/es/connections.ts @@ -3,12 +3,12 @@ import type { Messages } from "../en"; export const connections: Messages["connections"] = { title: "Herramientas conectadas", intro: - "Herramientas tuyas que tus bots pueden usar además de las que Botloft ya les da, como un lector de LinkedIn o un sistema de tu empresa. Cada bot recibe solo las que activas para él, y aun así te pregunta antes de usarlas.", + "Herramientas tuyas que tus agentes pueden usar además de las que Botloft ya les da, como un lector de LinkedIn o un sistema de tu empresa. Cada agente recibe solo las que activas para él, y aun así te pregunta antes de usarlas.", loadFailed: "No se pudieron leer las herramientas conectadas", none: "Aún no hay herramientas conectadas.", add: "Conectar una herramienta…", usedBy: (names) => `La usan ${names}`, - unused: "Ningún bot la usa todavía", + unused: "Ningún agente la usa todavía", kinds: { stdio: "Un programa en este equipo", http: "Una herramienta en una dirección", @@ -22,12 +22,12 @@ export const connections: Messages["connections"] = { name: "Nombre", nameHint: "Solo hace falta cuando la configuración pegada viene sin nombre.", purpose: "¿Para qué sirve? (opcional)", - purposeHint: "Tus bots leen esto para saber cuándo usarla.", + purposeHint: "Tus agentes leen esto para saber cuándo usarla.", found: (count) => (count === 1 ? "Encontré 1 herramienta." : `Encontré ${count} herramientas.`), program: (name) => - `${name} es un programa. Se ejecutará en tu equipo con tus permisos, fuera de las carpetas que Botloft guarda para tus bots. Conecta solo programas en los que confíes.`, + `${name} es un programa. Se ejecutará en tu equipo con tus permisos, fuera de las carpetas que Botloft guarda para tus agentes. Conecta solo programas en los que confíes.`, address: (name) => - `${name} es una herramienta en una dirección. Lo que tu bot le envíe sale de este equipo.`, + `${name} es una herramienta en una dirección. Lo que tu agente le envíe sale de este equipo.`, command: "Lo que ejecuta", understood: "Lo entiendo y confío en ella", connect: "Conectar", @@ -53,7 +53,7 @@ export const connections: Messages["connections"] = { }, removing: { title: (name) => `¿Quitar ${name}?`, - text: "Los bots que la usan la pierden y arrancan de nuevo cuando estén libres. Lo que permitiste para siempre con ella se olvida.", + text: "Los agentes que la usan la pierden y arrancan de nuevo cuando estén libres. Lo que permitiste para siempre con ella se olvida.", confirm: "Quitar", failed: "No se pudo quitar la herramienta", }, @@ -61,7 +61,7 @@ export const connections: Messages["connections"] = { title: "Herramientas conectadas", none: "Aún no hay herramientas conectadas. Puedes conectarlas en Ajustes, en Herramientas conectadas.", toggle: (tool, bot) => `${bot} puede usar ${tool}`, - failed: "No se pudieron cambiar las herramientas de este bot", + failed: "No se pudieron cambiar las herramientas de este agente", reason: "Por qué", }, states: { diff --git a/app/src/i18n/es/crewTemplates.ts b/app/src/i18n/es/crewTemplates.ts index 93e52389..ccc92900 100644 --- a/app/src/i18n/es/crewTemplates.ts +++ b/app/src/i18n/es/crewTemplates.ts @@ -4,15 +4,15 @@ export const crewTemplates: typeof en = { title: "Empezar con un modelo de equipo", intro: "Elige un modelo de equipo listo para trabajar, o empieza con un equipo vacío.", scratch: "Empezar con un equipo vacío", - scratchHint: "Solo el jefe. Tú añades los bots.", - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), + scratchHint: "Solo el jefe. Tú añades los agentes.", + bots: (count: number) => (count === 1 ? "1 agente" : `${count} agentes`), adds: "Este modelo trae", change: "Elegir otro modelo", back: "Volver", partial: (failed: number) => failed === 1 - ? "El equipo se creó, pero no se pudo añadir 1 bot. Puedes añadirlo desde la Agencia de bots." - : `El equipo se creó, pero no se pudieron añadir ${failed} bots. Puedes añadirlos desde la Agencia de bots.`, + ? "El equipo se creó, pero no se pudo añadir 1 agente. Puedes añadirlo desde la Agencia de agentes." + : `El equipo se creó, pero no se pudieron añadir ${failed} agentes. Puedes añadirlos desde la Agencia de agentes.`, loadFailed: "No se pudieron cargar los modelos", items: { "content-studio": { diff --git a/app/src/i18n/es/crews.ts b/app/src/i18n/es/crews.ts index a974385c..50fb9ed1 100644 --- a/app/src/i18n/es/crews.ts +++ b/app/src/i18n/es/crews.ts @@ -2,7 +2,7 @@ import type { Messages } from "../en"; export const crews: Messages["crews"] = { newCrew: "Nuevo equipo", - newBot: "Nuevo bot", + newBot: "Nuevo agente", rename: "Renombrar", paused: "En pausa", sidebar: { @@ -20,7 +20,7 @@ export const crews: Messages["crews"] = { expandAll: "Expandir todos los equipos", collapse: (crew: string) => `Contraer ${crew}`, expand: (crew: string) => `Expandir ${crew}`, - waiting: "un bot te necesita", + waiting: "un agente te necesita", filters: { label: "Filtrar conversaciones", all: "Todas", @@ -34,14 +34,14 @@ export const crews: Messages["crews"] = { working: (count: number) => (count === 1 ? "1 trabajando" : `${count} trabajando`), waiting: (count: number) => (count === 1 ? "1 te necesita" : `${count} te necesitan`), calm: "Todo tranquilo", - noBots: "Ningún bot todavía", + noBots: "Ningún agente todavía", }, dialog: { color: "Color", noColor: "Sin color", swatch: (color: string) => `Color ${color}`, folder: "Carpeta de trabajo", - folderHint: "Donde los bots guardan lo que hacen. Puede ser una carpeta que ya usas.", + folderHint: "Donde los agentes guardan lo que hacen. Puede ser una carpeta que ya usas.", folderDefault: "Una carpeta nueva dentro de Botloft", chooseFolder: "Elegir carpeta…", pickTitle: "Elige dónde trabaja el equipo", @@ -49,20 +49,20 @@ export const crews: Messages["crews"] = { goal: "¿Para qué es este equipo?", goalPlaceholder: "Crear y mantener la web de mi panadería", goalHint: - "El equipo empieza con un jefe, que lee esto, planea el trabajo y sugiere los bots que necesita.", + "El equipo empieza con un jefe, que lee esto, planea el trabajo y sugiere los agentes que necesita.", chiefModel: "Modelo del jefe", chiefName: "Jefe", - chiefRole: "Dirige el equipo: planea el trabajo, sugiere bots nuevos y reparte las tareas", + chiefRole: "Dirige el equipo: planea el trabajo, sugiere agentes nuevos y reparte las tareas", renameTitle: "Renombrar equipo", create: "Crear equipo", name: "Nombre", namePlaceholder: "Investigación", - createHint: "Los bots de un equipo pueden enviarse mensajes y comparten una carpeta.", + createHint: "Los agentes de un equipo pueden enviarse mensajes y comparten una carpeta.", renameHint: (slug: string) => `La carpeta conserva su nombre (${slug}).`, }, view: { - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), - pausedNote: "en pausa: sus bots siguen detenidos hasta que lo reanudes", + bots: (count: number) => (count === 1 ? "1 agente" : `${count} agentes`), + pausedNote: "en pausa: sus agentes siguen detenidos hasta que lo reanudes", pause: "Pausar equipo", resume: "Reanudar equipo", moreActions: "Más acciones del equipo", @@ -72,35 +72,35 @@ export const crews: Messages["crews"] = { changeFolder: "Cambiar carpeta de trabajo…", moveTitle: (crew: string) => `¿Mover ${crew} a otra carpeta?`, moveBody: (path: string) => - `Los bots trabajarán en ${path}. Cada uno se reinicia cuando termine lo que está haciendo. Lo que ya hicieron se queda donde está.`, + `Los agentes trabajarán en ${path}. Cada uno se reinicia cuando termine lo que está haciendo. Lo que ya hicieron se queda donde está.`, move: "Mover", archive: "Archivar equipo", delete: "Eliminar equipo", tabs: { label: "Vistas del equipo", - bots: "Bots", + bots: "Agentes", timeline: "Línea de tiempo", tasks: "Tareas", }, timelineEmpty: - "Aún no hay mensajes. Escríbele a un bot abajo; lo que los bots se envían entre sí también aparece aquí.", + "Aún no hay mensajes. Escríbele a un agente abajo; lo que los agentes se envían entre sí también aparece aquí.", archiveTitle: (crew: string) => `¿Archivar ${crew}?`, archiveBody: (bots: number) => bots === 0 ? "El equipo sale de la app." - : `El equipo sale de la app. ${bots === 1 ? "Su bot se detiene" : `Sus ${bots} bots se detienen`}, y los mensajes que aún ${bots === 1 ? "lo" : "los"} esperan no se entregan.`, + : `El equipo sale de la app. ${bots === 1 ? "Su agente se detiene" : `Sus ${bots} agentes se detienen`}, y los mensajes que aún ${bots === 1 ? "lo" : "los"} esperan no se entregan.`, deleteTitle: (crew: string) => `¿Eliminar ${crew}?`, deleteBody: (crew: string, bots: number, running: boolean) => bots === 0 ? `${crew} sale de Botloft para siempre. No se puede deshacer.` - : `${crew} y ${bots === 1 ? "su bot" : `sus ${bots} bots`} ${running ? "se detienen ahora y salen" : "salen"} de Botloft para siempre, con sus conversaciones, rutinas y tareas. No se puede deshacer.`, + : `${crew} y ${bots === 1 ? "su agente" : `sus ${bots} agentes`} ${running ? "se detienen ahora y salen" : "salen"} de Botloft para siempre, con sus conversaciones, rutinas y tareas. No se puede deshacer.`, deleteKept: - "Las carpetas siguen en tu ordenador, con todo lo que contienen: la carpeta de cada bot y la carpeta de trabajo del equipo:", + "Las carpetas siguen en tu ordenador, con todo lo que contienen: la carpeta de cada agente y la carpeta de trabajo del equipo:", deleteRecycle: "Enviar las carpetas del equipo a la Papelera", deleteRecycled: - "La carpeta del equipo va a la Papelera, con la carpeta de cada bot y la carpeta de trabajo dentro. Todavía puedes recuperarla de allí:", + "La carpeta del equipo va a la Papelera, con la carpeta de cada agente y la carpeta de trabajo dentro. Todavía puedes recuperarla de allí:", deleteRecycledChosen: - "La carpeta de cada bot va a la Papelera, de donde todavía puedes recuperarla. La carpeta de trabajo que elegiste se queda donde está:", + "La carpeta de cada agente va a la Papelera, de donde todavía puedes recuperarla. La carpeta de trabajo que elegiste se queda donde está:", failed: { pause: "No se pudo pausar el equipo", resume: "No se pudo reanudar el equipo", @@ -111,9 +111,9 @@ export const crews: Messages["crews"] = { }, }, bots: { - empty: (crew: string) => `Aún no hay bots en ${crew}.`, + empty: (crew: string) => `Aún no hay agentes en ${crew}.`, emptyHint: - "Un bot es una sesión de Claude Code que sigue en marcha, con su propia carpeta y su rol.", + "Un agente es una sesión de Claude Code que sigue en marcha, con su propia carpeta y su rol.", noRole: "Aún sin rol.", }, tasks: { @@ -121,8 +121,8 @@ export const crews: Messages["crews"] = { open: "Abiertas", all: "Todas", list: "Tareas", - noOpen: "No hay tareas abiertas. Los bots se crean tareas entre sí con send_message.", - none: "Aún no hay tareas. Los bots se crean tareas entre sí con send_message.", + noOpen: "No hay tareas abiertas. Los agentes se crean tareas entre sí con send_message.", + none: "Aún no hay tareas. Los agentes se crean tareas entre sí con send_message.", status: { open: "Abierta", done: "Completada", @@ -130,7 +130,7 @@ export const crews: Messages["crews"] = { cancelled: "Cancelada", expired: "Vencida", }, - archivedBot: "un bot archivado", + archivedBot: "un agente archivado", asked: "le pidió a", hop: (hops: number) => `paso ${hops}`, hopHint: "Posición en una cadena de delegaciones", diff --git a/app/src/i18n/es/desktop.ts b/app/src/i18n/es/desktop.ts index 6d952ee8..62b1380b 100644 --- a/app/src/i18n/es/desktop.ts +++ b/app/src/i18n/es/desktop.ts @@ -34,7 +34,7 @@ export const desktop: Messages["desktop"] = { emptyBody: "Cuando lea o use una ventana de una app que permitiste, la ventana aparece aquí, en vivo.", shortcut: - "Ctrl+Alt+Esc detiene a todos los bots en tu escritorio, incluso con Botloft cerrado.", + "Ctrl+Alt+Esc detiene a todos los agentes en tu escritorio, incluso con Botloft cerrado.", waiting: "Esperando la imagen…", read: "Leyó la ventana", click: (target) => `Hizo clic en "${target}"`, @@ -63,7 +63,7 @@ export const desktop: Messages["desktop"] = { realPoints: (bot) => [ `Cuando la accesibilidad no alcanza algo en esa app, ${bot} mueve tu cursor y escribe como si fueras tú.`, `Mientras actúa, el mouse y el teclado son suyos, y un aviso en tu pantalla lo dice.`, - `Mueve el mouse o presiona una tecla y se detiene al instante. Ctrl+Alt+Esc detiene a todos los bots en tu escritorio.`, + `Mueve el mouse o presiona una tecla y se detiene al instante. Ctrl+Alt+Esc detiene a todos los agentes en tu escritorio.`, `Nunca escribe en campos de contraseña ni presiona teclas propias de Windows, como la tecla Windows o Alt+Tab.`, ], realConfirm: "Activar", diff --git a/app/src/i18n/es/messages.ts b/app/src/i18n/es/messages.ts index 8aa6c67e..adc55277 100644 --- a/app/src/i18n/es/messages.ts +++ b/app/src/i18n/es/messages.ts @@ -2,7 +2,7 @@ import type { Messages } from "../en"; export const messages: Messages["messages"] = { calls: { - label: "Bots llamándose entre sí", + label: "Agentes llamándose entre sí", caller: (bot) => `${bot}:`, calling: (bot) => `Llamando a ${bot}`, picked: (bot) => `${bot} respondió`, @@ -10,15 +10,16 @@ export const messages: Messages["messages"] = { composer: { messageTo: "Mensaje para", recipient: "Destinatario", - placeholder: (handle: string | undefined) => `Escribe a @${handle ?? "bot"}. Ctrl+Enter envía.`, + placeholder: (handle: string | undefined) => + `Escribe a @${handle ?? "agente"}. Ctrl+Enter envía.`, send: "Enviar", }, delivery: { delivered: "Entregado", read: "Leído", - readTitle: "El bot empezó a trabajar en él", + readTitle: "El agente empezó a trabajar en él", delivering: "Entregando", - waiting: "Esperando al bot", + waiting: "Esperando al agente", retrying: (when: string, attempts: number) => `Nuevo intento ${when}, tras ${attempts} ${attempts === 1 ? "intento fallido" : "intentos fallidos"}`, notDelivered: "No entregado", @@ -30,14 +31,14 @@ export const messages: Messages["messages"] = { title: "Mensajes no entregados", retryAll: "Reintentar todos", explanation: - "Botloft dejó de insistir tras varios intentos. Al reintentar, el mensaje vuelve a la cola; se envía cuando el bot esté listo.", + "Botloft dejó de insistir tras varios intentos. Al reintentar, el mensaje vuelve a la cola; se envía cuando el agente esté listo.", to: (name: string) => `Para ${name}`, tries: (count: number) => `${count} ${count === 1 ? "intento" : "intentos"}`, }, row: { you: "Tú", system: "Botloft", - goneBot: "un bot que ya no está en el equipo", + goneBot: "un agente que ya no está en el equipo", to: "para", task: "Tarea", result: "Resultado", diff --git a/app/src/i18n/es/onboarding.ts b/app/src/i18n/es/onboarding.ts index 70f66a9b..9c93ddcf 100644 --- a/app/src/i18n/es/onboarding.ts +++ b/app/src/i18n/es/onboarding.ts @@ -9,18 +9,18 @@ export const onboarding: Messages["onboarding"] = { install: "Preparando Botloft…", update: "Actualizando Botloft…", restart: "Reiniciando Botloft…", - start: "Iniciando tus bots…", + start: "Iniciando tus agentes…", }, installNote: (system: string) => - `Botloft ejecuta tus bots en segundo plano. En Configuración eliges si siguen trabajando después de cerrar esta ventana y si Botloft se inicia con ${system}.`, + `Botloft ejecuta tus agentes en segundo plano. En Configuración eliges si siguen trabajando después de cerrar esta ventana y si Botloft se inicia con ${system}.`, stopped: { title: "Botloft no pudo iniciar", - body: "Botloft ejecuta tus bots en segundo plano, y esa parte no se inició.", + body: "Botloft ejecuta tus agentes en segundo plano, y esa parte no se inició.", notRunning: "No está en ejecución.", }, outdated: { title: "Botloft no pudo terminar de actualizarse", - body: "La parte que ejecuta tus bots en segundo plano sigue en la versión anterior.", + body: "La parte que ejecuta tus agentes en segundo plano sigue en la versión anterior.", running: (version: string) => `Versión en ejecución: ${version}.`, stillOld: (version: string) => `Sigue informando la versión ${version}.`, }, @@ -45,34 +45,35 @@ export const onboarding: Messages["onboarding"] = { welcome: { title: "Te damos la bienvenida a Botloft", intro: - "Un equipo es un grupo de bots que siguen funcionando, se escriben entre sí y comparten una carpeta. Cada bot corre en el agente que elijas: Claude Code, u otro que actives. Cada equipo empieza con un jefe: dile para qué sirve el equipo, y él planea el trabajo y sugiere los bots que necesita.", + "Un equipo es un grupo de agentes que siguen funcionando, se escriben entre sí y comparten una carpeta. Cada agente corre en el motor que elijas: Claude Code, u otro que actives. Cada equipo empieza con un jefe: dile para qué sirve el equipo, y él planea el trabajo y sugiere los agentes que necesita.", botloft: "Botloft", running: (system: string) => - `Funcionando en segundo plano. Se inicia con ${system}, así que tus bots siguen trabajando después de cerrar esta ventana.`, + `Funcionando en segundo plano. Se inicia con ${system}, así que tus agentes siguen trabajando después de cerrar esta ventana.`, runningNotAtStart: - "Funcionando en segundo plano, así que tus bots siguen trabajando después de cerrar esta ventana.", + "Funcionando en segundo plano, así que tus agentes siguen trabajando después de cerrar esta ventana.", runningWhileOpen: - "Funcionando mientras esta ventana está abierta. Al cerrarla, tus bots se detienen.", + "Funcionando mientras esta ventana está abierta. Al cerrarla, tus agentes se detienen.", claudeCode: "Claude Code", checking: "Comprobando…", version: (version: string) => `Versión ${version}`, account: "Cuenta de Claude", signedIn: "Sesión iniciada.", - signedOut: "Tus bots trabajan con tu cuenta de Claude. Inicia sesión una vez y quedan listos.", + signedOut: + "Tus agentes trabajan con tu cuenta de Claude. Inicia sesión una vez y quedan listos.", ready: "listo", notReady: "no está listo", stillChecking: "comprobando", - askFirstTitle: "Los bots preguntan antes de cambiar cosas", + askFirstTitle: "Los agentes preguntan antes de cambiar cosas", askFirstBody: - "Cuando un bot quiere ejecutar un comando o editar un archivo, lo pregunta en su chat y espera a que lo permitas o lo deniegues.", + "Cuando un agente quiere ejecutar un comando o editar un archivo, lo pregunta en su chat y espera a que lo permitas o lo deniegues.", ideasTitle: "O toca una idea", - claudeOptional: "No instalado. Solo lo necesitas para los bots que corren en Claude Code.", + claudeOptional: "No instalado. Solo lo necesitas para los agentes que corren en Claude Code.", agentMissing: "No encontrado. Instálalo y abre Botloft otra vez.", notNeeded: "no hace falta", createCrew: "Crear tu primer equipo", }, claudeCode: { - help: "Botloft ejecuta tus bots con Claude Code. Instálalo o actualízalo, ábrelo una vez para iniciar sesión, y Botloft lo detecta en menos de 30 segundos.", + help: "Botloft ejecuta tus agentes con Claude Code. Instálalo o actualízalo, ábrelo una vez para iniciar sesión, y Botloft lo detecta en menos de 30 segundos.", install: "Cómo instalar Claude Code", openFailed: "No se pudo abrir el enlace", }, diff --git a/app/src/i18n/es/phone.ts b/app/src/i18n/es/phone.ts index 9cdce08c..8ea6090f 100644 --- a/app/src/i18n/es/phone.ts +++ b/app/src/i18n/es/phone.ts @@ -6,7 +6,7 @@ export const phone: Messages["phone"] = { pair: { title: "Conectar este móvil", intro: - "Este móvil aprobará las solicitudes de tus bots, responderá a sus preguntas y hablará con ellos, desde cualquier lugar. Los archivos se quedan en tu ordenador.", + "Este móvil aprobará las solicitudes de tus agentes, responderá a sus preguntas y hablará con ellos, desde cualquier lugar. Los archivos se quedan en tu ordenador.", nameLabel: "Nombre de este móvil", connect: "Conectar", connecting: "Conectando…", @@ -23,7 +23,7 @@ export const phone: Messages["phone"] = { inbox: { title: "Esperándote", empty: "Nada te está esperando", - emptyBody: "Cuando un bot te necesite, aparecerá aquí.", + emptyBody: "Cuando un agente te necesite, aparecerá aquí.", loading: "Viendo qué está esperando…", computerOff: "El ordenador está apagado o sin internet. Las solicitudes siguen esperando allí.", noConnection: "Sin conexión. Intentando de nuevo…", @@ -32,9 +32,9 @@ export const phone: Messages["phone"] = { }, tabs: { requests: "Solicitudes", chats: "Conversaciones", newReply: "respuesta nueva" }, chats: { - empty: "Aún no hay bots", - emptyBody: "Cuando crees un bot en el ordenador, aparece aquí.", - loading: "Cargando los bots…", + empty: "Aún no hay agentes", + emptyBody: "Cuando crees un agente en el ordenador, aparece aquí.", + loading: "Cargando los agentes…", working: "trabajando…", kind: { owner: "Tú", @@ -48,7 +48,7 @@ export const phone: Messages["phone"] = { inCrew: (crew: string) => `en ${crew}`, allCrews: "Todos", crewsLabel: "Equipos", - bots: (n: number) => `${n} ${n === 1 ? "bot" : "bots"}`, + bots: (n: number) => `${n} ${n === 1 ? "agente" : "agentes"}`, back: "Conversaciones", loadingChat: "Cargando la conversación…", nothing: "Nada aquí todavía. Escribe el primer mensaje.", @@ -69,19 +69,19 @@ export const phone: Messages["phone"] = { }, pending: { sending: "Enviando…", - sent: "Enviado. El bot lo verá cuando pueda.", + sent: "Enviado. El agente lo verá cuando pueda.", notSent: "No enviado.", reasons: { offline: "Revisa la conexión e inténtalo de nuevo.", timeout: "El ordenador no respondió. Mira si está encendido e inténtalo de nuevo.", - invalid: "El bot no acepta este texto.", - not_found: "Este bot ya no existe.", + invalid: "El agente no acepta este texto.", + not_found: "Este agente ya no existe.", rate_limited: "Demasiados mensajes en un minuto. Espera un poco.", failed: "Algo salió mal. Inténtalo de nuevo.", } as Record, }, reach: - "Un móvil conectado y desbloqueado puede leer las conversaciones de tus bots y escribirles.", + "Un móvil conectado y desbloqueado puede leer las conversaciones de tus agentes y escribirles.", }, approval: { asks: (bot: string) => `${bot} pide`, @@ -92,7 +92,7 @@ export const phone: Messages["phone"] = { allow: "Permitir", deny: "Denegar", sending: "Enviando…", - noteLabel: "Nota para el bot (opcional)", + noteLabel: "Nota para el agente (opcional)", cut: "Demasiado larga para leerla en el móvil. Aquí solo puedes denegarla, o responderla en el ordenador.", atComputer: "Esta solicitud te necesita en el ordenador. Aquí solo puedes denegarla.", failed: "No se envió. Comprueba la conexión e inténtalo de nuevo.", @@ -115,7 +115,7 @@ export const phone: Messages["phone"] = { notices: { turnOn: "Activar los avisos", turnOff: "Desactivar los avisos", - on: "Los avisos están activados. Recibes uno cuando un bot te necesite.", + on: "Los avisos están activados. Recibes uno cuando un agente te necesite.", off: "Los avisos están desactivados.", blocked: "Los avisos están bloqueados para esta página. Permítelos en los ajustes del navegador.", diff --git a/app/src/i18n/es/questions.ts b/app/src/i18n/es/questions.ts index 48e3ed8f..09b57fab 100644 --- a/app/src/i18n/es/questions.ts +++ b/app/src/i18n/es/questions.ts @@ -21,16 +21,16 @@ export const questions: Messages["questions"] = { label: "Preguntas", open: (count: number) => count === 0 - ? "Preguntas de tus bots" + ? "Preguntas de tus agentes" : count === 1 ? "1 pregunta te espera" : `${count} preguntas te esperan`, - title: "Preguntas de tus bots", + title: "Preguntas de tus agentes", intro: - "Cuando un bot necesita una decisión tuya para seguir, pregunta aquí y sigue trabajando. Tu respuesta le llega como mensaje.", + "Cuando un agente necesita una decisión tuya para seguir, pregunta aquí y sigue trabajando. Tu respuesta le llega como mensaje.", emptyTitle: "Ninguna pregunta te espera", emptyBody: - "Los bots preguntan aquí cuando te necesitan, incluso de madrugada, en una rutina. Responde cuando puedas.", + "Los agentes preguntan aquí cuando te necesitan, incluso de madrugada, en una rutina. Responde cuando puedas.", openChat: (bot: string) => `Abrir el chat con ${bot}`, inCrew: (crew: string) => `en ${crew}`, }, diff --git a/app/src/i18n/es/routines.ts b/app/src/i18n/es/routines.ts index 2e9e4885..072b2245 100644 --- a/app/src/i18n/es/routines.ts +++ b/app/src/i18n/es/routines.ts @@ -8,7 +8,7 @@ export const routines: Messages["routines"] = { empty: (bot: string) => `${bot} todavía no tiene rutinas.`, emptyHint: (bot: string) => `Una rutina hace que ${bot} trabaje solo a horas fijas, como "días laborables a las 09:00", y el resultado aparece en el chat.`, - crewEmpty: "Ningún bot de este equipo tiene rutinas todavía. Abre un bot para crear una.", + crewEmpty: "Ningún agente de este equipo tiene rutinas todavía. Abre un agente para crear una.", newRoutine: "Nueva rutina", toggle: (name: string) => `${name} encendida o apagada`, runNow: "Ejecutar ahora", @@ -26,7 +26,7 @@ export const routines: Messages["routines"] = { done: "La última salió bien", failed: "La última falló", overlap: "La última vez se saltó: la anterior no había terminado", - bot_paused: "La última vez se saltó: el bot estaba en pausa", + bot_paused: "La última vez se saltó: el agente estaba en pausa", too_soon: "El último aviso se saltó: llegó menos de 5 minutos después del anterior", signalFrom: (bot: string) => `aviso de ${bot}`, missed: (count: number) => @@ -36,7 +36,7 @@ export const routines: Messages["routines"] = { }, page: { title: "Rutinas", - lead: "Lo que tus bots hacen solos, en todos los equipos.", + lead: "Lo que tus agentes hacen solos, en todos los equipos.", upcoming: "Próximas", watching: "Vigilando", showMore: (count) => `Mostrar ${count} más`, @@ -44,7 +44,7 @@ export const routines: Messages["routines"] = { of: (bot) => `Rutinas de ${bot}`, emptyTitle: "Aún no hay rutinas", emptyBody: - "Una rutina hace que un bot trabaje solo a horas fijas. Pídeselo a un bot en el chat, o abre la pestaña Rutinas de un bot.", + "Una rutina hace que un agente trabaje solo a horas fijas. Pídeselo a un agente en el chat, o abre la pestaña Rutinas de un agente.", }, when: { everyDay: (time: string) => `Todos los días a las ${time}`, @@ -54,7 +54,7 @@ export const routines: Messages["routines"] = { everyMinutes: (count: number) => (count === 1 ? "Cada minuto" : `Cada ${count} minutos`), everyHours: (count: number) => (count === 1 ? "Cada hora" : `Cada ${count} horas`), cron: (expr: string) => `Cron: ${expr}`, - signal: (name: string) => `Cuando un bot avise “${name}”`, + signal: (name: string) => `Cuando un agente avise “${name}”`, }, tag: (name: string) => `Rutina · ${name}`, dialog: { @@ -70,7 +70,7 @@ export const routines: Messages["routines"] = { weekdays: "Días laborables", days: "Días elegidos", interval: "Cada…", - signal: "Cuando un bot avise", + signal: "Cuando un agente avise", }, at: "A las", every: "Cada", @@ -90,7 +90,7 @@ export const routines: Messages["routines"] = { signal: "Aviso", signalPlaceholder: "informe-listo", signalHint: (bot: string) => - `${bot} hace esto cuando un bot del equipo manda este aviso. Dile a ese bot, en sus instrucciones, cuándo avisar, como “avisa informe-listo cuando guardes el informe”.`, + `${bot} hace esto cuando un agente del equipo manda este aviso. Dile a ese agente, en sus instrucciones, cuándo avisar, como “avisa informe-listo cuando guardes el informe”.`, cronHint: "Minuto, hora, día del mes, mes y día de la semana. Sustituye a Cuándo.", create: "Crear rutina", save: "Guardar", @@ -108,10 +108,10 @@ export const routines: Messages["routines"] = { }, summary: { title: "Resumen diario", - hint: "Cada día el jefe te cuenta qué hizo cada bot, qué está parado y qué te está esperando.", + hint: "Cada día el jefe te cuenta qué hizo cada agente, qué está parado y qué te está esperando.", name: "Resumen diario", prompt: - "El día se acaba. Usa crew_activity (las últimas 24 horas) y escríbeme un resumen corto del día: qué terminó cada bot, qué sigue abierto o atrasado, qué falló y qué me está esperando (aprobaciones y preguntas), lo más importante primero. Palabras sencillas, como máximo 12 líneas. Si no pasó nada, dilo en una línea. No empieces trabajo nuevo ni escribas a otros bots por esto.", + "El día se acaba. Usa crew_activity (las últimas 24 horas) y escríbeme un resumen corto del día: qué terminó cada agente, qué sigue abierto o atrasado, qué falló y qué me está esperando (aprobaciones y preguntas), lo más importante primero. Palabras sencillas, como máximo 12 líneas. Si no pasó nada, dilo en una línea. No empieces trabajo nuevo ni escribas a otros agentes por esto.", time: "Hora", turnOn: "Activar", turnOff: "Desactivar", diff --git a/app/src/i18n/es/search.ts b/app/src/i18n/es/search.ts index 28852b59..e7960d88 100644 --- a/app/src/i18n/es/search.ts +++ b/app/src/i18n/es/search.ts @@ -9,7 +9,7 @@ export const search: Messages["search"] = { placeholder: "Busca palabras en tus chats", scope: "Dónde buscar", allCrews: "Todos los equipos", - hint: "Encuentra palabras en lo que tú, otros bots y Botloft escribieron a tus bots, en lo que respondieron y en sus preguntas.", + hint: "Encuentra palabras en lo que tú, otros agentes y Botloft escribieron a tus agentes, en lo que respondieron y en sus preguntas.", tooShort: "Escribe al menos 2 letras", results: "Resultados", nothing: (query: string) => `No se encontró nada para “${query}”`, @@ -19,6 +19,6 @@ export const search: Messages["search"] = { inCrew: (crew: string) => `en ${crew}`, you: "Tú", botloft: "Botloft", - goneBot: "Un bot que ya no está en el equipo", + goneBot: "Un agente que ya no está en el equipo", openHere: (bot: string) => `Abrir el chat con ${bot} en este punto`, }; diff --git a/app/src/i18n/es/setup.ts b/app/src/i18n/es/setup.ts index 05d216a3..f6f5a020 100644 --- a/app/src/i18n/es/setup.ts +++ b/app/src/i18n/es/setup.ts @@ -1,7 +1,7 @@ import type { Messages } from "../en"; export const setup: Messages["setup"] = { - tagline: "Bots siempre encendidos que trabajan juntos para ti.", + tagline: "Agentes siempre encendidos que trabajan juntos para ti.", install: "Instalar", update: "Actualizar", forYou: "Se instala solo para ti, sin pedir un administrador.", @@ -10,7 +10,7 @@ export const setup: Messages["setup"] = { newer: (installed: string) => `Ya tienes una versión más nueva (${installed}).`, open: "Abrir Botloft", reinstall: "Instalar de nuevo", - appOpen: "Botloft está abierto y se cierra para instalar. Tus bots siguen trabajando.", + appOpen: "Botloft está abierto y se cierra para instalar. Tus agentes siguen trabajando.", installing: "Instalando Botloft…", installingLine: "Solo tarda unos segundos.", progress: "Instalando", diff --git a/app/src/i18n/es/shell.ts b/app/src/i18n/es/shell.ts index bbf94a9a..3bc14420 100644 --- a/app/src/i18n/es/shell.ts +++ b/app/src/i18n/es/shell.ts @@ -13,8 +13,8 @@ export const shell: Messages["shell"] = { home: "Inicio", }, sidebar: { - hide: "Ocultar la lista de bots", - show: "Mostrar la lista de bots", + hide: "Ocultar la lista de agentes", + show: "Mostrar la lista de agentes", }, zoom: { level: (percent: number, isDefault: boolean) => @@ -30,9 +30,9 @@ export const shell: Messages["shell"] = { }, loadFailed: "No se pudieron cargar tus equipos", recycleFailed: (path: string) => `La carpeta ${path} no fue a la Papelera y sigue allí`, - botsCantStart: "Los bots no pueden iniciar", + botsCantStart: "Los agentes no pueden iniciar", signIn: { title: "Inicia sesión en Claude", - body: "Tus bots trabajan con tu cuenta de Claude. Inicia sesión una vez y empiezan solos.", + body: "Tus agentes trabajan con tu cuenta de Claude. Inicia sesión una vez y empiezan solos.", }, }; diff --git a/app/src/i18n/es/tools.ts b/app/src/i18n/es/tools.ts index 3b347baa..6945cfe7 100644 --- a/app/src/i18n/es/tools.ts +++ b/app/src/i18n/es/tools.ts @@ -32,12 +32,12 @@ export const tools: Messages["tools"] = { send_message: "Enviar un mensaje", complete_task: "Terminar una tarea", my_tasks: "Ver sus tareas", - suggest_bot: "Sugerir un bot", + suggest_bot: "Sugerir un agente", schedule_routine: "Crear una rutina", my_routines: "Ver mis rutinas", change_routine: "Cambiar una rutina", delete_routine: "Borrar una rutina", - change_bot: "Cambiar un bot", + change_bot: "Cambiar un agente", ask_crew_access: "Acceder a otro equipo", share_file: "Compartir un archivo", ask_owner: "Hacerte una pregunta", diff --git a/app/src/i18n/es/updates.ts b/app/src/i18n/es/updates.ts index 3cf8a9e7..e3721032 100644 --- a/app/src/i18n/es/updates.ts +++ b/app/src/i18n/es/updates.ts @@ -4,7 +4,7 @@ export const updates: Messages["updates"] = { available: "Actualización disponible", title: "Actualizar Botloft", ready: (version: string) => - `Botloft ${version} está listo. Botloft se cierra, instala la actualización y se vuelve a abrir. Tus bots se detienen un momento y continúan donde lo dejaron.`, + `Botloft ${version} está listo. Botloft se cierra, instala la actualización y se vuelve a abrir. Tus agentes se detienen un momento y continúan donde lo dejaron.`, whatsNew: "Novedades", later: "Más tarde", updateNow: "Actualizar ahora", diff --git a/app/src/i18n/pt-BR/account.ts b/app/src/i18n/pt-BR/account.ts index 67bbbc12..60a14949 100644 --- a/app/src/i18n/pt-BR/account.ts +++ b/app/src/i18n/pt-BR/account.ts @@ -15,8 +15,8 @@ export const account: Messages["account"] = { usage: { title: "Uso", intro: - "Quanto do seu plano do Claude os seus bots já usaram. Atualiza sempre que um bot trabalha.", - empty: "O uso aparece depois da primeira resposta de um bot.", + "Quanto do seu plano do Claude os seus agentes já usaram. Atualiza sempre que um agente trabalha.", + empty: "O uso aparece depois da primeira resposta de um agente.", windows: { five_hour: "Janela de 5 horas", seven_day: "Esta semana", @@ -25,12 +25,12 @@ export const account: Messages["account"] = { }, used: (percent: number) => `${percent}% usado`, resets: (relative: string) => `Renova ${relative}`, - limited: "Limite atingido. Seus bots esperam até ele renovar.", + limited: "Limite atingido. Seus agentes esperam até ele renovar.", updated: (relative: string) => `Atualizado ${relative}`, tokens: { - title: "Uso por bot", + title: "Uso por agente", intro: - "Quanto do seu plano semanal cada bot usou, e os tokens por trás disso: os pedaços de texto que um bot lê e escreve.", + "Quanto do seu plano semanal cada agente usou, e os tokens por trás disso: os pedaços de texto que um agente lê e escreve.", period: "Período", periods: { hour: "Última hora", @@ -39,25 +39,25 @@ export const account: Messages["account"] = { month: "30 dias", all: "Tudo", }, - empty: "Nenhum bot trabalhou nesse período.", + empty: "Nenhum agente trabalhou nesse período.", failed: "Não deu para carregar os tokens", archived: "arquivado", /** `crew` is null on the total of all bots. */ detail: (crew: string | null, times: number) => `${crew === null ? "" : `${crew} · `}Trabalhou ${times === 1 ? "1 vez" : `${times} vezes`}`, - total: "Todos os bots", + total: "Todos os agentes", share: (percent) => `≈ ${percent} da semana`, tokensUsed: (count) => `${count} tokens`, estimate: - "≈ Estimativa: o Botloft aprende quanto do seu plano o trabalho dos bots consome pelo quanto o plano semanal sobe enquanto eles trabalham. O que você usa fora do Botloft também faz o plano subir, então os bots podem aparecer com um pouco mais do que usaram.", + "≈ Estimativa: o Botloft aprende quanto do seu plano o trabalho dos agentes consome pelo quanto o plano semanal sobe enquanto eles trabalham. O que você usa fora do Botloft também faz o plano subir, então os agentes podem aparecer com um pouco mais do que usaram.", learning: - "A parte de cada bot no seu plano semanal aparece depois que o plano subir alguns pontos com os bots trabalhando. Até lá, os tokens.", + "A parte de cada agente no seu plano semanal aparece depois que o plano subir alguns pontos com os agentes trabalhando. Até lá, os tokens.", }, }, backup: { exportTitle: "Salvar uma cópia", exportIntro: - "Guarda suas equipes, bots, conversas e a memória de cada bot num arquivo trancado com uma senha, para trazer de volta depois de reinstalar ou em outro computador. Pastas de trabalho que você escolheu fora do Botloft não entram.", + "Guarda suas equipes, agentes, conversas e a memória de cada agente num arquivo trancado com uma senha, para trazer de volta depois de reinstalar ou em outro computador. Pastas de trabalho que você escolheu fora do Botloft não entram.", passphrase: "Senha", passphraseHint: "Ao menos 8 caracteres. Sem ela a cópia não abre, e o Botloft não a guarda em lugar nenhum.", @@ -79,7 +79,7 @@ export const account: Messages["account"] = { opening: "Abrindo…", from: (when) => `Cópia de ${when}`, crew: (name, bots) => - `${name}: ${bots === 0 ? "nenhum bot" : bots === 1 ? "1 bot" : `${bots} bots`}`, + `${name}: ${bots === 0 ? "nenhum agente" : bots === 1 ? "1 agente" : `${bots} agentes`}`, workFolders: "Estas pastas de trabalho não estão na cópia. Se este é outro computador, ponha os arquivos delas de volta:", replaces: "Tudo neste Botloft é trocado. O Botloft reinicia para isso.", @@ -89,7 +89,7 @@ export const account: Messages["account"] = { openFailed: "Não foi possível abrir a cópia", restoreFailed: "Não foi possível restaurar a cópia", lightNote: - "Esta cópia tem as equipes, os bots, a memória deles e as rotinas. As conversas e os arquivos não estão nela.", + "Esta cópia tem as equipes, os agentes, a memória deles e as rotinas. As conversas e os arquivos não estão nela.", reasons: { short_passphrase: "A senha precisa de ao menos 8 caracteres.", wrong_passphrase: "A senha está errada, ou o arquivo está danificado.", @@ -117,7 +117,7 @@ export const account: Messages["account"] = { usage: (used: string, quota: string) => `${used} de ${quota} usados`, uploadTitle: "Enviar uma cópia para a nuvem", uploadIntro: - "Sobem: suas equipes, os bots, a memória de cada um e as rotinas. As conversas e os arquivos ficam só neste computador. A cópia é trancada com uma senha que nunca sai dele.", + "Sobem: suas equipes, os agentes, a memória de cada um e as rotinas. As conversas e os arquivos ficam só neste computador. A cópia é trancada com uma senha que nunca sai dele.", upload: "Enviar uma cópia agora", uploading: "Enviando…", uploaded: (size: string) => `Cópia enviada (${size}).`, @@ -180,10 +180,10 @@ export const account: Messages["account"] = { mobile: { title: "Celular", intro: - "Aprove os pedidos dos seus bots, responda às perguntas deles e converse com eles de qualquer lugar. Os arquivos ficam só neste computador.", + "Aprove os pedidos dos seus agentes, responda às perguntas deles e converse com eles de qualquer lugar. Os arquivos ficam só neste computador.", needAccount: "Entre na conta acima para usar o celular.", warning: - "Quem estiver com um celular conectado e desbloqueado pode ler as conversas dos seus bots, escrever para eles e permitir pedidos até você desconectá-lo. Um PIN no celular ajuda; você liga nele.", + "Quem estiver com um celular conectado e desbloqueado pode ler as conversas dos seus agentes, escrever para eles e permitir pedidos até você desconectá-lo. Um PIN no celular ajuda; você liga nele.", connect: "Conectar um celular", starting: "Preparando…", qrLabel: "Código para ler com o celular", @@ -210,55 +210,57 @@ export const account: Messages["account"] = { title: "Configurações", background: "Em segundo plano", keepWorking: "Continuar trabalhando depois de fechar o Botloft", - keepWorkingOn: "Seus bots seguem trabalhando e respondendo depois que você fecha esta janela.", + keepWorkingOn: + "Seus agentes seguem trabalhando e respondendo depois que você fecha esta janela.", keepWorkingOff: - "Ao fechar o Botloft, todos os bots param. Eles continuam de onde pararam quando você abrir de novo.", + "Ao fechar o Botloft, todos os agentes param. Eles continuam de onde pararam quando você abrir de novo.", tray: "Mostrar o Botloft perto do relógio", trayOn: - "Com a janela fechada, o ícone fica perto do relógio: um clique abre o Botloft, e ele avisa quando um bot precisa de você.", - trayOff: "Fechar a janela fecha o Botloft. Os bots seguem trabalhando, sem ícone nem avisos.", + "Com a janela fechada, o ícone fica perto do relógio: um clique abre o Botloft, e ele avisa quando um agente precisa de você.", + trayOff: + "Fechar a janela fecha o Botloft. Os agentes seguem trabalhando, sem ícone nem avisos.", startWithSystem: (system: string) => `Iniciar com o ${system}`, startWithSystemOn: (system: string) => - `Quando você entra no ${system}, seus bots voltam a trabalhar sozinhos, sem abrir esta janela.`, + `Quando você entra no ${system}, seus agentes voltam a trabalhar sozinhos, sem abrir esta janela.`, startWithSystemOff: - "Depois que você reinicia o computador, os bots esperam você abrir o Botloft.", + "Depois que você reinicia o computador, os agentes esperam você abrir o Botloft.", openAtSignIn: (system: string) => `Abrir a janela ao entrar no ${system}`, openAtSignInOn: (system: string) => `A janela do Botloft abre quando você entra no ${system}.`, openAtSignInNearClock: "O Botloft começa perto do relógio, sem abrir a janela.", openAtSignInOff: "A janela só abre quando você abrir o Botloft.", alerts: "Avisos", - notifyNeeds: "Quando um bot precisar de você", + notifyNeeds: "Quando um agente precisar de você", notifyNeedsHint: (system: string) => - `Um aviso do ${system} quando um bot pede permissão ou precisa que você entre na conta, se o Botloft não estiver na frente.`, - notifyDone: "Quando um bot terminar", - notifyDoneHint: "Um aviso quando um bot termina o que estava fazendo.", - markReplies: "Marcar o ícone quando um bot responder", + `Um aviso do ${system} quando um agente pede permissão ou precisa que você entre na conta, se o Botloft não estiver na frente.`, + notifyDone: "Quando um agente terminar", + notifyDoneHint: "Um aviso quando um agente termina o que estava fazendo.", + markReplies: "Marcar o ícone quando um agente responder", markRepliesHint: "Um ponto no ícone do Botloft na barra de tarefas enquanto houver conversa que você não leu.", sound: "Tocar um som", soundHint: "Um som curto com cada aviso, também com o Botloft na frente.", appSounds: "Sons do app", appSoundsHint: - "Sons suaves com o Botloft na frente: mensagem enviada, resposta ou arquivo no chat aberto, bot ou equipe nova.", + "Sons suaves com o Botloft na frente: mensagem enviada, resposta ou arquivo no chat aberto, agente ou equipe nova.", alertsNeedTray: "Com a janela fechada, os avisos só chegam com o Botloft perto do relógio (em Geral).", - keepAwake: "Não deixar o computador dormir enquanto os bots trabalham", + keepAwake: "Não deixar o computador dormir enquanto os agentes trabalham", keepAwakeHint: "Ele ainda dorme quando você fecha a tampa ou escolhe Suspender.", saveFailed: "Não foi possível mudar a configuração", pages: "Partes das configurações", general: "Geral", - defaultAgent: "Agente dos bots novos", + defaultAgent: "Os agentes novos rodam em", defaultAgentHint: - "Vale para bots novos, o chefe de uma equipe nova e os bots que um modelo de equipe adiciona. Cada bot guarda o agente com que foi criado.", + "Vale para agentes novos, o chefe de uma equipe nova e os agentes que um modelo de equipe adiciona. Cada agente guarda o motor com que foi criado.", chat: "Conversa", enterSends: "Enter envia a mensagem", enterSendsOn: "Shift+Enter começa uma nova linha.", enterSendsOff: "Enter começa uma nova linha, e Ctrl+Enter envia.", - followBot: "Abrir o navegador e as telas quando um bot começar a usar", - followBotOn: "O painel abre ao lado da conversa, para você ver o que o bot faz.", + followBot: "Abrir o navegador e as telas quando um agente começar a usar", + followBotOn: "O painel abre ao lado da conversa, para você ver o que o agente faz.", followBotOff: "O botão do painel ganha um ponto, e você abre quando quiser.", approvalWait: "Quanto tempo um pedido de permissão espera por você", - approvalWaitHint: "Sem resposta nesse tempo, o pedido do bot é negado e ele segue sem isso.", + approvalWaitHint: "Sem resposta nesse tempo, o pedido do agente é negado e ele segue sem isso.", waitFor: (minutes: number) => minutes < 60 || minutes % 60 !== 0 ? `${minutes} min` @@ -276,12 +278,12 @@ export const account: Messages["account"] = { language: "Idioma", archived: "Arquivados", archivedIntro: - "Os bots e as equipes que você arquivou estão parados e fora da vista, e o Botloft ainda guarda as conversas deles. Exclua um para tirá-lo de vez.", + "Os agentes e as equipes que você arquivou estão parados e fora da vista, e o Botloft ainda guarda as conversas deles. Exclua um para tirá-lo de vez.", archivedEmpty: "Nada está arquivado.", archivedLoadFailed: "Não foi possível carregar o que está arquivado", archivedCrew: (bots: number, when: string) => - `Equipe · ${bots === 1 ? "1 bot" : `${bots} bots`} · arquivada ${when}`, - archivedBot: (crew: string, when: string) => `Bot de ${crew} · arquivado ${when}`, + `Equipe · ${bots === 1 ? "1 agente" : `${bots} agentes`} · arquivada ${when}`, + archivedBot: (crew: string, when: string) => `Agente de ${crew} · arquivado ${when}`, deleteArchived: "Excluir", deleteArchivedOne: (name: string) => `Excluir ${name}`, about: "Sobre", diff --git a/app/src/i18n/pt-BR/alerts.ts b/app/src/i18n/pt-BR/alerts.ts index d4e31129..5093dea9 100644 --- a/app/src/i18n/pt-BR/alerts.ts +++ b/app/src/i18n/pt-BR/alerts.ts @@ -5,10 +5,10 @@ import type { Messages } from "../en"; export const alerts: Messages["alerts"] = { tray: { tooltip: (status) => `Botloft: ${status}`, - working: (count) => (count === 1 ? "1 bot trabalhando" : `${count} bots trabalhando`), + working: (count) => (count === 1 ? "1 agente trabalhando" : `${count} agentes trabalhando`), needsYou: (bot) => `${bot} precisa de você`, waiting: "Algo está esperando por você", - idle: "Nenhum bot trabalhando", + idle: "Nenhum agente trabalhando", open: "Abrir o Botloft", pause: "Pausar todas as equipes", resume: "Retomar todas as equipes", diff --git a/app/src/i18n/pt-BR/bots.ts b/app/src/i18n/pt-BR/bots.ts index f74e9885..4f739090 100644 --- a/app/src/i18n/pt-BR/bots.ts +++ b/app/src/i18n/pt-BR/bots.ts @@ -16,7 +16,7 @@ export const bots: Messages["bots"] = { }, auth_error: { label: "Precisa entrar", - hint: "O Claude Code não está conectado a uma conta, ou a conta não pode ser usada. Entre no Claude e o bot volta a iniciar sozinho. Já entrou? Confira seu plano do Claude e depois reinicie o bot.", + hint: "O Claude Code não está conectado a uma conta, ou a conta não pode ser usada. Entre no Claude e o agente volta a iniciar sozinho. Já entrou? Confira seu plano do Claude e depois reinicie o agente.", }, backoff: { label: "Reiniciando", @@ -27,7 +27,7 @@ export const bots: Messages["bots"] = { }, chief: { badge: "Chefe", - hint: (crew: string) => `Lidera ${crew}: planeja o trabalho e sugere bots novos`, + hint: (crew: string) => `Lidera ${crew}: planeja o trabalho e sugere agentes novos`, }, header: { noRole: "Sem função definida", @@ -36,7 +36,7 @@ export const bots: Messages["bots"] = { restart: "Reiniciar", showDetails: "Mostrar detalhes", hideDetails: "Ocultar detalhes", - more: "Mais ações do bot", + more: "Mais ações do agente", menuOf: (name: string) => `Ações de ${name}`, edit: "Editar", restartFresh: "Reiniciar com uma nova conversa", @@ -45,16 +45,16 @@ export const bots: Messages["bots"] = { stopChief: "Deixar de ser chefe", markUnread: "Marcar como não lida", markRead: "Marcar como lida", - archive: "Arquivar bot", - delete: "Excluir bot", + archive: "Arquivar agente", + delete: "Excluir agente", failed: { - pause: "Não foi possível pausar o bot", - resume: "Não foi possível retomar o bot", - restart: "Não foi possível reiniciar o bot", + pause: "Não foi possível pausar o agente", + resume: "Não foi possível retomar o agente", + restart: "Não foi possível reiniciar o agente", openFolder: "Não foi possível abrir a pasta", chief: "Não foi possível trocar o chefe", - archive: "Não foi possível arquivar o bot", - delete: "Não foi possível excluir o bot", + archive: "Não foi possível arquivar o agente", + delete: "Não foi possível excluir o agente", }, fresh: { title: "Começar uma nova conversa?", @@ -64,12 +64,12 @@ export const bots: Messages["bots"] = { }, archiveConfirm: { title: (name: string) => `Arquivar ${name}?`, - confirm: "Arquivar bot", - body: "O bot para e sai da equipe. As mensagens que ainda esperam por ele não são entregues.", + confirm: "Arquivar agente", + body: "O agente para e sai da equipe. As mensagens que ainda esperam por ele não são entregues.", }, deleteConfirm: { title: (name: string) => `Excluir ${name}?`, - confirm: "Excluir bot", + confirm: "Excluir agente", removed: (name: string, running: boolean) => `${name} ${running ? "para agora e sai" : "sai"} do Botloft de vez, com a conversa, as rotinas e as tarefas de que fazia parte. Não dá para desfazer.`, chief: (crew: string) => `${crew} fica sem chefe.`, @@ -81,44 +81,44 @@ export const bots: Messages["bots"] = { }, }, dialog: { - newTitle: "Novo bot", + newTitle: "Novo agente", editTitle: (name: string) => `Editar ${name}`, - create: "Criar bot", + create: "Criar agente", save: "Salvar", name: "Nome", namePlaceholder: "Revisor", - nameHint: "Os outros bots falam com ele pelo @ criado a partir deste nome.", + nameHint: "Os outros agentes falam com ele pelo @ criado a partir deste nome.", role: "Função", rolePlaceholder: "Revisa pull requests antes do merge", instructions: "Instruções", instructionsPlaceholder: - "Como este bot trabalha, o que ele pode fazer sozinho e quando deve perguntar.", - instructionsHint: "Salvas agora nas regras do bot; ele as lê na próxima vez que iniciar.", + "Como este agente trabalha, o que ele pode fazer sozinho e quando deve perguntar.", + instructionsHint: "Salvas agora nas regras do agente; ele as lê na próxima vez que iniciar.", fullAccess: { title: "Rodar qualquer comando sem perguntar", off: "Desligado: ele só roda os comandos da lista abaixo.", - on: "Ligado: ele roda qualquer comando sem perguntar a você, e um comando pode ler ou mudar tudo o que você pode. Só para um bot em que você confia.", + on: "Ligado: ele roda qualquer comando sem perguntar a você, e um comando pode ler ou mudar tudo o que você pode. Só para um agente em que você confia.", }, commands: { - title: "Comandos que este bot pode rodar", + title: "Comandos que este agente pode rodar", placeholder: "git status\nnpm test", - hint: "Um comando por linha; ele pode rodar estes e o que começar com eles, sem perguntar a você. Ele não consegue perguntar, então todo o resto é recusado. Um * sozinho deixa rodar qualquer coisa. O bot reinicia para usar a lista.", + hint: "Um comando por linha; ele pode rodar estes e o que começar com eles, sem perguntar a você. Ele não consegue perguntar, então todo o resto é recusado. Um * sozinho deixa rodar qualquer coisa. O agente reinicia para usar a lista.", }, agent: { - title: "Agente", + title: "Roda em", names: { claude: "Claude Code", agy: "Antigravity (experimental)", codex: "Codex", }, experimental: - "Experimental. Este bot não consegue perguntar a você antes de agir. Ele trabalha com arquivos das próprias pastas e usa as ferramentas da equipe, mas não roda comandos, e não tem medidor de uso nem escolha de modelo.", + "Experimental. Este agente não consegue perguntar a você antes de agir. Ele trabalha com arquivos das próprias pastas e usa as ferramentas da equipe, mas não roda comandos, e não tem medidor de uso nem escolha de modelo.", experimentalCodex: - "Experimental. Este bot pergunta a você antes de mudar um arquivo ou rodar um comando, como o Claude Code. Ele ainda pode ler qualquer arquivo.", + "Experimental. Este agente pergunta a você antes de mudar um arquivo ou rodar um comando, como o Claude Code. Ele ainda pode ler qualquer arquivo.", }, color: "Cor", swatch: (color: string) => `Cor ${color}`, - colorUnset: "Se você não escolher, o bot recebe a próxima cor da equipe.", + colorUnset: "Se você não escolher, o agente recebe a próxima cor da equipe.", custom: "Escolher qualquer cor", picker: { area: "Saturação e brilho", @@ -131,7 +131,7 @@ export const bots: Messages["bots"] = { }, notices: { crewPaused: (crew: string) => `A equipe ${crew} está pausada`, - crewPausedBody: "Os bots dela ficam parados até você retomar a equipe.", + crewPausedBody: "Os agentes dela ficam parados até você retomar a equipe.", }, details: { title: (name: string) => `Sobre ${name}`, diff --git a/app/src/i18n/pt-BR/browser.ts b/app/src/i18n/pt-BR/browser.ts index 26925edb..0fc5c270 100644 --- a/app/src/i18n/pt-BR/browser.ts +++ b/app/src/i18n/pt-BR/browser.ts @@ -33,7 +33,7 @@ export const browser: Messages["browser"] = { "Quando ele pesquisar ou usar um site, você vê aqui ao vivo: cada página que ele abre, cada clique.", starting: "Abrindo o navegador…", closed: "Navegador fechado", - closedBody: "Ele abre de novo quando o bot precisar. Os logins continuam.", + closedBody: "Ele abre de novo quando o agente precisar. Os logins continuam.", openForMe: "Abrir para mim", openForMeWhy: (bot) => `Para ver o que ${bot} deixou aberto e mexer você mesmo. ${bot} não é acordado nem avisado; devolva quando terminar e o navegador volta a descansar.`, @@ -41,8 +41,8 @@ export const browser: Messages["browser"] = { failedTitle: "O navegador não abriu", failedBody: (system: string) => system === "Windows" - ? "O Botloft usa o Microsoft Edge, que vem com o Windows. Confira se ele está instalado e peça ao bot para tentar de novo." - : "O Botloft usa o Google Chrome, o Chromium ou o Microsoft Edge. Confira se um deles está instalado e peça ao bot para tentar de novo.", + ? "O Botloft usa o Microsoft Edge, que vem com o Windows. Confira se ele está instalado e peça ao agente para tentar de novo." + : "O Botloft usa o Google Chrome, o Chromium ou o Microsoft Edge. Confira se um deles está instalado e peça ao agente para tentar de novo.", details: "Detalhes", tabs: { label: "Abas", @@ -130,7 +130,7 @@ export const browser: Messages["browser"] = { help: { needs: (bot) => `${bot} precisa de você no navegador`, asks: (bot, task) => `${bot} pede: ${task}`, - why: "Assuma o controle, faça isso na página e devolva. Se o site não deixar entrar por ali, use Entrar numa janela no painel do navegador. O bot não vê o que você digita em campos de senha.", + why: "Assuma o controle, faça isso na página e devolva. Se o site não deixar entrar por ali, use Entrar numa janela no painel do navegador. O agente não vê o que você digita em campos de senha.", take: "Assumir o navegador", done: "Pronto", wontDo: "Não vou fazer", diff --git a/app/src/i18n/pt-BR/catalog.ts b/app/src/i18n/pt-BR/catalog.ts index 812d55ce..e0505e4b 100644 --- a/app/src/i18n/pt-BR/catalog.ts +++ b/app/src/i18n/pt-BR/catalog.ts @@ -16,17 +16,17 @@ import { sectors } from "./catalogSectors"; import { security } from "./catalogSecurity"; export const catalog: Messages["catalog"] = { - title: "Agência de bots", - open: "Agência de bots", - openHint: "Bots prontos que você pode adicionar a esta equipe", + title: "Agência de agentes", + open: "Agência de agentes", + openHint: "Agentes prontos que você pode adicionar a esta equipe", intro: - "Escolha um bot para o trabalho. Ele entra nesta equipe pronto para trabalhar, e depois você pode mudar o que quiser nele.", + "Escolha um agente para o trabalho. Ele entra nesta equipe pronto para trabalhar, e depois você pode mudar o que quiser nele.", invite: { title: "Quem você quer na sua equipe?", - body: "Não sabe o que criar? Escolha um bot para o trabalho. Depois você pode mudar o que quiser nele.", + body: "Não sabe o que criar? Escolha um agente para o trabalho. Depois você pode mudar o que quiser nele.", }, - search: "Buscar bots", - filter: "Tipos de bot", + search: "Buscar agentes", + filter: "Tipos de agente", categories: { all: "Todos", code: "Código", @@ -44,23 +44,23 @@ export const catalog: Messages["catalog"] = { games: "Jogos", legal: "Jurídico", }, - none: "Nenhum bot combina com isso.", - loading: "Carregando os bots…", + none: "Nenhum agente combina com isso.", + loading: "Carregando os agentes…", add: "Adicionar na equipe", learnMore: "Saber mais", - back: "Voltar para todos os bots", - seeAll: "Ver todos os bots", + back: "Voltar para todos os agentes", + seeAll: "Ver todos os agentes", joined: (name) => `${name} entrou na equipe`, customize: "Personalizar", failed: { - load: "Não deu para carregar os bots", - add: "Não deu para adicionar o bot", + load: "Não deu para carregar os agentes", + add: "Não deu para adicionar o agente", }, detail: { what: "O que faz", when: "Quando chamar", pairs: "Combina bem com", - technical: "O que o bot recebe quando começa", + technical: "O que o agente recebe quando começa", }, roles: { ...base, diff --git a/app/src/i18n/pt-BR/catalogBase.ts b/app/src/i18n/pt-BR/catalogBase.ts index 736286e0..803807f3 100644 --- a/app/src/i18n/pt-BR/catalogBase.ts +++ b/app/src/i18n/pt-BR/catalogBase.ts @@ -13,10 +13,10 @@ export const base: typeof en = { }, "code-reviewer": { name: "Revisor de código", - role: "Revisa o que os outros bots mudaram e aponta erros, riscos e complicação à toa", + role: "Revisa o que os outros agentes mudaram e aponta erros, riscos e complicação à toa", summary: "Lê as mudanças com olhar crítico e diz o que está errado ou arriscado", about: - "Lê as mudanças que outro bot fez e lista o que pode quebrar, o que é arriscado e o que está mais complicado do que precisa. Ele nunca edita o código, só relata, e a decisão é sua. Pensa mais fundo que a maioria dos bots, então gasta um pouco mais do seu plano.", + "Lê as mudanças que outro agente fez e lista o que pode quebrar, o que é arriscado e o que está mais complicado do que precisa. Ele nunca edita o código, só relata, e a decisão é sua. Pensa mais fundo que a maioria dos agentes, então gasta um pouco mais do seu plano.", when: "Depois que um Desenvolvedor muda algo importante: um pagamento, um login, qualquer coisa difícil de desfazer.", pairs: "Desenvolvedor, de quem ele revisa o trabalho, e Testador.", }, diff --git a/app/src/i18n/pt-BR/catalogDesign.ts b/app/src/i18n/pt-BR/catalogDesign.ts index 100e16b6..341af665 100644 --- a/app/src/i18n/pt-BR/catalogDesign.ts +++ b/app/src/i18n/pt-BR/catalogDesign.ts @@ -7,7 +7,7 @@ export const design: typeof en = { summary: "Escreve um guia curto da marca com o seu material e confere se tudo segue o guia", about: "Reúne seu logo, cores, fontes e exemplos num guia de uma página, marcando o que viu e o que sugere. Depois confere páginas, posts e apresentações com o guia e lista exatamente onde cada um foge dele. Nunca inventa um fato da marca nem muda uma regra sem perguntar.", - when: "Quando você tem uma marca e várias pessoas ou bots criam coisas para ela.", + when: "Quando você tem uma marca e várias pessoas ou agentes criam coisas para ela.", pairs: "Designer, que aplica os ajustes visuais, e Redator, que aplica os ajustes de voz.", }, "ui-designer": { diff --git a/app/src/i18n/pt-BR/catalogEngineering.ts b/app/src/i18n/pt-BR/catalogEngineering.ts index ddc77397..0d372eba 100644 --- a/app/src/i18n/pt-BR/catalogEngineering.ts +++ b/app/src/i18n/pt-BR/catalogEngineering.ts @@ -68,7 +68,7 @@ export const engineering: typeof en = { summary: "Procura falhas de segurança no seu código e na sua configuração e explica como corrigir", about: - "Revisa o seu código e a sua configuração atrás de falhas, explica cada uma em palavras simples com a gravidade e a menor correção. Trabalha na defesa, só no que é seu, e nunca repete um segredo que encontrar. Pensa mais fundo que a maioria dos bots, então gasta um pouco mais do seu plano.", + "Revisa o seu código e a sua configuração atrás de falhas, explica cada uma em palavras simples com a gravidade e a menor correção. Trabalha na defesa, só no que é seu, e nunca repete um segredo que encontrar. Pensa mais fundo que a maioria dos agentes, então gasta um pouco mais do seu plano.", when: "Antes de lançar, depois de uma mudança grande, ou quando você lida com dados de outras pessoas.", pairs: "Desenvolvedor e Engenheiro DevOps, que aplicam as correções, e Revisor de código.", }, diff --git a/app/src/i18n/pt-BR/catalogProduct.ts b/app/src/i18n/pt-BR/catalogProduct.ts index 700367d0..eed330c8 100644 --- a/app/src/i18n/pt-BR/catalogProduct.ts +++ b/app/src/i18n/pt-BR/catalogProduct.ts @@ -47,8 +47,8 @@ export const product: typeof en = { summary: "Mantém o trabalho da equipe andando em passos pequenos, com acompanhamento e retrospectivas honestas", about: - "Mantém um quadro simples do que vem a seguir, do que está em andamento e do que está pronto, pede atualizações rápidas aos bots e conduz uma retrospectiva ao fim de cada rodada para mudar uma coisa.", - when: "Quando a equipe tem muitos bots e o trabalho vive travando ou sendo esquecido.", + "Mantém um quadro simples do que vem a seguir, do que está em andamento e do que está pronto, pede atualizações rápidas aos agentes e conduz uma retrospectiva ao fim de cada rodada para mudar uma coisa.", + when: "Quando a equipe tem muitos agentes e o trabalho vive travando ou sendo esquecido.", pairs: "Gerente de projetos, para o plano, e Secretário de reuniões.", }, "goals-coach": { diff --git a/app/src/i18n/pt-BR/chat.ts b/app/src/i18n/pt-BR/chat.ts index 4dd1d35d..24ecb608 100644 --- a/app/src/i18n/pt-BR/chat.ts +++ b/app/src/i18n/pt-BR/chat.ts @@ -39,13 +39,13 @@ export const chat: Messages["chat"] = { showInFolder: (file: string) => `Mostrar ${file} na pasta`, showInFolderHint: "Mostrar na pasta", openFolderFailed: "Não foi possível abrir a pasta", - missing: "Não está mais na pasta do bot", + missing: "Não está mais na pasta do agente", loading: (file: string) => `Carregando ${file}`, }, inbound: { task: "Tarefa", result: "Resultado", - goneBot: "Um bot que não está mais na equipe", + goneBot: "Um agente que não está mais na equipe", routine: "Mostrar o que a rotina pede", }, run: { @@ -56,7 +56,7 @@ export const chat: Messages["chat"] = { `Levou ${time}${ tokens === null ? "" : `. Leu ${tokens.read} tokens e escreveu ${tokens.wrote}.` }`, - stopped: (reason: string) => `O bot parou de trabalhar nisto: ${reason}`, + stopped: (reason: string) => `O agente parou de trabalhar nisto: ${reason}`, }, tools: { label: "Chamadas de ferramentas", @@ -110,7 +110,7 @@ export const chat: Messages["chat"] = { `${bot} não disse para que serve este comando. Na dúvida, negue e pergunte.`, command: "Ver o comando", noteLabel: (bot: string) => `Observação para ${bot} se você negar`, - notePlaceholder: "Por que não? Vai para o bot se você negar (opcional)", + notePlaceholder: "Por que não? Vai para o agente se você negar (opcional)", allow: "Permitir", deny: "Negar", always: { @@ -152,12 +152,12 @@ export const chat: Messages["chat"] = { bypassBody: (bot: string) => `${bot} vai editar arquivos, rodar comandos e usar a internet neste computador sem pedir sua permissão antes.`, bypassRisk: - "Ele não fica preso à pasta dele: pode ler e alterar seus outros arquivos, os arquivos de outros bots e os do próprio Botloft. Uma mensagem de outra pessoa pode levá-lo a fazer algo que você não queria.", + "Ele não fica preso à pasta dele: pode ler e alterar seus outros arquivos, os arquivos de outros agentes e os do próprio Botloft. Uma mensagem de outra pessoa pode levá-lo a fazer algo que você não queria.", bypassChief: (bot: string) => - `${bot} é o chefe da equipe, então também vai criar bots novos sem perguntar.`, - bypassAdvice: "Ative só para um bot em quem você confia para tudo, e só enquanto precisar.", + `${bot} é o chefe da equipe, então também vai criar agentes novos sem perguntar.`, + bypassAdvice: "Ative só para um agente em quem você confia para tudo, e só enquanto precisar.", badge: "Não pergunta nada", - badgeHint: "Este bot faz tudo sem perguntar. Mude isso no seletor de modo, embaixo do chat.", + badgeHint: "Este agente faz tudo sem perguntar. Mude isso no seletor de modo, embaixo do chat.", }, model: { title: "Modelo", @@ -193,7 +193,7 @@ export const chat: Messages["chat"] = { loading: "Carregando os modelos…", listFailed: "Não foi possível carregar a lista de modelos", failed: "Não foi possível trocar o modelo", - defaultHint: (bot: string) => `${bot} usa o modelo padrão do próprio agente`, + defaultHint: (bot: string) => `${bot} usa o modelo padrão do próprio motor`, later: (bot: string, model: string) => `${bot} troca para ${model}.`, }, effort: { @@ -243,7 +243,7 @@ export const chat: Messages["chat"] = { noAuto: "Ela não se compacta sozinha.", byItself: "O Codex compacta sozinho quando a conversa chega perto de encher.", estimate: - "Uma estimativa: este agente não informa o tamanho da janela, então o tamanho vem da família do modelo.", + "Uma estimativa: este motor não informa o tamanho da janela, então o tamanho vem da família do modelo.", compact: "Compactar agora", compactHint: (bot: string) => `Troca o que veio antes por um resumo: ${bot} volta a ter espaço e cada mensagem gasta menos.`, @@ -253,7 +253,7 @@ export const chat: Messages["chat"] = { failed: "Não foi possível compactar a conversa", }, suggestion: { - title: (bot: string) => `${bot} sugere um bot novo`, + title: (bot: string) => `${bot} sugere um agente novo`, why: "Por quê", name: "Nome", role: "Função", @@ -262,9 +262,9 @@ export const chat: Messages["chat"] = { startsNow: (bot: string) => `Ele começa na hora e recebe o trabalho de ${bot}.`, noteLabel: (bot: string) => `O que dizer a ${bot} se você recusar`, notePlaceholder: (bot: string) => `Se recusar, diga a ${bot} o porquê (opcional)`, - create: "Criar bot", + create: "Criar agente", decline: "Agora não", - createFailed: "Não foi possível criar o bot", + createFailed: "Não foi possível criar o agente", declineFailed: "Não foi possível enviar a resposta", created: (name: string) => `Você criou ${name}`, declined: (name: string) => `Você recusou ${name}`, @@ -326,7 +326,7 @@ export const chat: Messages["chat"] = { read: (whom) => `Ler os arquivos de ${whom} e da pasta de trabalho`, edit: (whom) => `Mudar e criar arquivos de ${whom} e da pasta de trabalho`, why: (bot) => `Por quê, segundo ${bot}:`, - explain: "As equipes não se enxergam. Só agora vale até o bot terminar esta vez.", + explain: "As equipes não se enxergam. Só agora vale até o agente terminar esta vez.", once: "Só agora", alwaysBot: (other) => `Sempre ${other}`, alwaysCrew: (crew) => `Sempre a equipe ${crew}`, @@ -346,7 +346,7 @@ export const chat: Messages["chat"] = { reason: (bot) => `Por quê, segundo ${bot}:`, newInstructions: "Ver as instruções novas", explain: - "A função, as instruções, o modelo e o esforço novos valem a partir do próximo início do bot, quando ele terminar o que está fazendo.", + "A função, as instruções, o modelo e o esforço novos valem a partir do próximo início do agente, quando ele terminar o que está fazendo.", apply: "Mudar", decline: "Agora não", noteLabel: (bot) => `O que dizer a ${bot} se você recusar`, @@ -359,7 +359,7 @@ export const chat: Messages["chat"] = { plan: { ready: (bot: string) => `${bot} fez um plano e quer seguir com ele`, noteLabel: (bot: string) => `O que ${bot} deve mudar no plano`, - notePlaceholder: "O que deve mudar? Vai para o bot se você pedir mudanças (opcional)", + notePlaceholder: "O que deve mudar? Vai para o agente se você pedir mudanças (opcional)", approve: "Aprovar plano", keepPlanning: "Pedir mudanças", approveFailed: "Não foi possível aprovar o plano", @@ -378,8 +378,8 @@ export const chat: Messages["chat"] = { usageLimit: "Seu plano do Claude atingiu o limite de uso. As mensagens ficam esperando até o limite renovar.", modelUnavailable: - "O modelo deste bot não está disponível: talvez não faça parte do seu plano do Claude. Escolha outro modelo abaixo do chat.", - turnFailed: (detail: string) => `O bot não conseguiu terminar isto: ${detail}`, + "O modelo deste agente não está disponível: talvez não faça parte do seu plano do Claude. Escolha outro modelo abaixo do chat.", + turnFailed: (detail: string) => `O agente não conseguiu terminar isto: ${detail}`, compacted: "A conversa foi compactada: o que veio antes agora é um resumo, e há espaço de novo.", autoCompacted: "A conversa ficou cheia e foi compactada: o que veio antes agora é um resumo.", diff --git a/app/src/i18n/pt-BR/connections.ts b/app/src/i18n/pt-BR/connections.ts index aae3cb37..69df2040 100644 --- a/app/src/i18n/pt-BR/connections.ts +++ b/app/src/i18n/pt-BR/connections.ts @@ -3,12 +3,12 @@ import type { Messages } from "../en"; export const connections: Messages["connections"] = { title: "Ferramentas conectadas", intro: - "Ferramentas suas que os bots podem usar além das que o Botloft já dá, como um leitor de LinkedIn ou um sistema da sua empresa. Cada bot recebe só as que você liga para ele, e ainda pergunta antes de usá-las.", + "Ferramentas suas que os agentes podem usar além das que o Botloft já dá, como um leitor de LinkedIn ou um sistema da sua empresa. Cada agente recebe só as que você liga para ele, e ainda pergunta antes de usá-las.", loadFailed: "Não deu para ler as ferramentas conectadas", none: "Nenhuma ferramenta conectada ainda.", add: "Conectar uma ferramenta…", usedBy: (names) => `Usada por ${names}`, - unused: "Nenhum bot a usa ainda", + unused: "Nenhum agente a usa ainda", kinds: { stdio: "Um programa neste computador", http: "Uma ferramenta em um endereço", @@ -23,12 +23,12 @@ export const connections: Messages["connections"] = { name: "Nome", nameHint: "Só é preciso quando as configurações coladas vêm sem nome.", purpose: "Para que serve? (opcional)", - purposeHint: "Os bots leem isto para saber quando usá-la.", + purposeHint: "Os agentes leem isto para saber quando usá-la.", found: (count) => (count === 1 ? "Achei 1 ferramenta." : `Achei ${count} ferramentas.`), program: (name) => - `${name} é um programa. Ele vai rodar no seu computador com as suas permissões, fora das pastas que o Botloft guarda para os seus bots. Conecte só programas em que você confia.`, + `${name} é um programa. Ele vai rodar no seu computador com as suas permissões, fora das pastas que o Botloft guarda para os seus agentes. Conecte só programas em que você confia.`, address: (name) => - `${name} é uma ferramenta em um endereço. O que o seu bot mandar para ela sai deste computador.`, + `${name} é uma ferramenta em um endereço. O que o seu agente mandar para ela sai deste computador.`, command: "O que ela executa", understood: "Entendi e confio nela", connect: "Conectar", @@ -54,7 +54,7 @@ export const connections: Messages["connections"] = { }, removing: { title: (name) => `Remover ${name}?`, - text: "Os bots que a usam a perdem e começam de novo quando estiverem livres. O que você permitiu para sempre com ela é esquecido.", + text: "Os agentes que a usam a perdem e começam de novo quando estiverem livres. O que você permitiu para sempre com ela é esquecido.", confirm: "Remover", failed: "Não deu para remover a ferramenta", }, @@ -62,7 +62,7 @@ export const connections: Messages["connections"] = { title: "Ferramentas conectadas", none: "Nenhuma ferramenta conectada ainda. Você pode conectá-las em Configurações, em Ferramentas conectadas.", toggle: (tool, bot) => `${bot} pode usar ${tool}`, - failed: "Não deu para mudar as ferramentas deste bot", + failed: "Não deu para mudar as ferramentas deste agente", reason: "Por quê", }, states: { diff --git a/app/src/i18n/pt-BR/crewTemplates.ts b/app/src/i18n/pt-BR/crewTemplates.ts index 879f93d3..7638b5b6 100644 --- a/app/src/i18n/pt-BR/crewTemplates.ts +++ b/app/src/i18n/pt-BR/crewTemplates.ts @@ -4,15 +4,15 @@ export const crewTemplates: typeof en = { title: "Começar com um modelo de equipe", intro: "Escolha um modelo de equipe pronto para trabalhar, ou comece com uma equipe vazia.", scratch: "Começar com uma equipe vazia", - scratchHint: "Só o chefe. Você adiciona os bots.", - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), + scratchHint: "Só o chefe. Você adiciona os agentes.", + bots: (count: number) => (count === 1 ? "1 agente" : `${count} agentes`), adds: "Este modelo traz", change: "Escolher outro modelo", back: "Voltar", partial: (failed: number) => failed === 1 - ? "A equipe foi criada, mas 1 bot não pôde ser adicionado. Você pode adicioná-lo pela Agência de bots." - : `A equipe foi criada, mas ${failed} bots não puderam ser adicionados. Você pode adicioná-los pela Agência de bots.`, + ? "A equipe foi criada, mas 1 agente não pôde ser adicionado. Você pode adicioná-lo pela Agência de agentes." + : `A equipe foi criada, mas ${failed} agentes não puderam ser adicionados. Você pode adicioná-los pela Agência de agentes.`, loadFailed: "Não foi possível carregar os modelos", items: { "content-studio": { diff --git a/app/src/i18n/pt-BR/crews.ts b/app/src/i18n/pt-BR/crews.ts index 09db0383..5a2fe6de 100644 --- a/app/src/i18n/pt-BR/crews.ts +++ b/app/src/i18n/pt-BR/crews.ts @@ -2,7 +2,7 @@ import type { Messages } from "../en"; export const crews: Messages["crews"] = { newCrew: "Nova equipe", - newBot: "Novo bot", + newBot: "Novo agente", rename: "Renomear", paused: "Pausada", sidebar: { @@ -20,7 +20,7 @@ export const crews: Messages["crews"] = { expandAll: "Expandir todas as equipes", collapse: (crew: string) => `Recolher ${crew}`, expand: (crew: string) => `Expandir ${crew}`, - waiting: "um bot precisa de você", + waiting: "um agente precisa de você", filters: { label: "Filtrar conversas", all: "Todas", @@ -34,14 +34,14 @@ export const crews: Messages["crews"] = { working: (count: number) => (count === 1 ? "1 trabalhando" : `${count} trabalhando`), waiting: (count: number) => (count === 1 ? "1 precisa de você" : `${count} precisam de você`), calm: "Tudo calmo", - noBots: "Nenhum bot ainda", + noBots: "Nenhum agente ainda", }, dialog: { color: "Cor", noColor: "Sem cor", swatch: (color: string) => `Cor ${color}`, folder: "Pasta de trabalho", - folderHint: "Onde os bots guardam o que fazem. Pode ser uma pasta que você já usa.", + folderHint: "Onde os agentes guardam o que fazem. Pode ser uma pasta que você já usa.", folderDefault: "Uma pasta nova dentro do Botloft", chooseFolder: "Escolher pasta…", pickTitle: "Escolha onde a equipe trabalha", @@ -49,21 +49,21 @@ export const crews: Messages["crews"] = { goal: "Para que é esta equipe?", goalPlaceholder: "Criar e manter o site da minha padaria", goalHint: - "A equipe começa com um Chefe, que lê isto, planeja o trabalho e sugere os bots de que precisa.", + "A equipe começa com um Chefe, que lê isto, planeja o trabalho e sugere os agentes de que precisa.", chiefModel: "Modelo do Chefe", chiefName: "Chefe", - chiefRole: "Lidera a equipe: planeja o trabalho, sugere bots novos e distribui as tarefas", + chiefRole: "Lidera a equipe: planeja o trabalho, sugere agentes novos e distribui as tarefas", renameTitle: "Renomear equipe", create: "Criar equipe", name: "Nome", namePlaceholder: "Pesquisa", createHint: - "Os bots de uma equipe podem mandar mensagens uns aos outros e compartilham uma pasta.", + "Os agentes de uma equipe podem mandar mensagens uns aos outros e compartilham uma pasta.", renameHint: (slug: string) => `A pasta mantém o nome (${slug}).`, }, view: { - bots: (count: number) => (count === 1 ? "1 bot" : `${count} bots`), - pausedNote: "pausada: os bots dela ficam parados até você retomá-la", + bots: (count: number) => (count === 1 ? "1 agente" : `${count} agentes`), + pausedNote: "pausada: os agentes dela ficam parados até você retomá-la", pause: "Pausar equipe", resume: "Retomar equipe", moreActions: "Mais ações da equipe", @@ -73,35 +73,35 @@ export const crews: Messages["crews"] = { changeFolder: "Mudar pasta de trabalho…", moveTitle: (crew: string) => `Mudar ${crew} para outra pasta?`, moveBody: (path: string) => - `Os bots vão trabalhar em ${path}. Cada um reinicia quando terminar o que está fazendo. O que já foi feito fica onde está.`, + `Os agentes vão trabalhar em ${path}. Cada um reinicia quando terminar o que está fazendo. O que já foi feito fica onde está.`, move: "Mudar", archive: "Arquivar equipe", delete: "Excluir equipe", tabs: { label: "Visões da equipe", - bots: "Bots", + bots: "Agentes", timeline: "Linha do tempo", tasks: "Tarefas", }, timelineEmpty: - "Nenhuma mensagem ainda. Escreva para um bot abaixo; o que os bots mandam uns aos outros também aparece aqui.", + "Nenhuma mensagem ainda. Escreva para um agente abaixo; o que os agentes mandam uns aos outros também aparece aqui.", archiveTitle: (crew: string) => `Arquivar ${crew}?`, archiveBody: (bots: number) => bots === 0 ? "A equipe sai do app." - : `A equipe sai do app. ${bots === 1 ? "O bot dela para" : `Os ${bots} bots dela param`}, e as mensagens que ainda esperam por ${bots === 1 ? "ele" : "eles"} não são entregues.`, + : `A equipe sai do app. ${bots === 1 ? "O agente dela para" : `Os ${bots} agentes dela param`}, e as mensagens que ainda esperam por ${bots === 1 ? "ele" : "eles"} não são entregues.`, deleteTitle: (crew: string) => `Excluir ${crew}?`, deleteBody: (crew: string, bots: number, running: boolean) => bots === 0 ? `${crew} sai do Botloft de vez. Não dá para desfazer.` - : `${crew} e ${bots === 1 ? "o bot dela" : `os ${bots} bots dela`} ${running ? "param agora e saem" : "saem"} do Botloft de vez, com as conversas, as rotinas e as tarefas. Não dá para desfazer.`, + : `${crew} e ${bots === 1 ? "o agente dela" : `os ${bots} agentes dela`} ${running ? "param agora e saem" : "saem"} do Botloft de vez, com as conversas, as rotinas e as tarefas. Não dá para desfazer.`, deleteKept: - "As pastas continuam no seu computador, com tudo o que está nelas: a pasta de cada bot e a pasta de trabalho da equipe:", + "As pastas continuam no seu computador, com tudo o que está nelas: a pasta de cada agente e a pasta de trabalho da equipe:", deleteRecycle: "Mandar as pastas da equipe para a Lixeira", deleteRecycled: - "A pasta da equipe vai para a Lixeira, com a pasta de cada bot e a pasta de trabalho dentro. Ainda dá para recuperar de lá:", + "A pasta da equipe vai para a Lixeira, com a pasta de cada agente e a pasta de trabalho dentro. Ainda dá para recuperar de lá:", deleteRecycledChosen: - "A pasta de cada bot vai para a Lixeira, de onde ainda dá para recuperar. A pasta de trabalho que você escolheu fica onde está:", + "A pasta de cada agente vai para a Lixeira, de onde ainda dá para recuperar. A pasta de trabalho que você escolheu fica onde está:", failed: { pause: "Não foi possível pausar a equipe", resume: "Não foi possível retomar a equipe", @@ -112,9 +112,9 @@ export const crews: Messages["crews"] = { }, }, bots: { - empty: (crew: string) => `Nenhum bot em ${crew} ainda.`, + empty: (crew: string) => `Nenhum agente em ${crew} ainda.`, emptyHint: - "Um bot é uma sessão do Claude Code que fica sempre rodando, com pasta e função próprias.", + "Um agente é uma sessão do Claude Code que fica sempre rodando, com pasta e função próprias.", noRole: "Sem função ainda.", }, tasks: { @@ -122,8 +122,8 @@ export const crews: Messages["crews"] = { open: "Abertas", all: "Todas", list: "Tarefas", - noOpen: "Nenhuma tarefa aberta. Os bots criam tarefas uns para os outros com send_message.", - none: "Nenhuma tarefa ainda. Os bots criam tarefas uns para os outros com send_message.", + noOpen: "Nenhuma tarefa aberta. Os agentes criam tarefas uns para os outros com send_message.", + none: "Nenhuma tarefa ainda. Os agentes criam tarefas uns para os outros com send_message.", status: { open: "Aberta", done: "Concluída", @@ -131,7 +131,7 @@ export const crews: Messages["crews"] = { cancelled: "Cancelada", expired: "Vencida", }, - archivedBot: "um bot arquivado", + archivedBot: "um agente arquivado", asked: "pediu a", hop: (hops: number) => `etapa ${hops}`, hopHint: "Posição numa cadeia de delegações", diff --git a/app/src/i18n/pt-BR/desktop.ts b/app/src/i18n/pt-BR/desktop.ts index ed782212..d6cc7c24 100644 --- a/app/src/i18n/pt-BR/desktop.ts +++ b/app/src/i18n/pt-BR/desktop.ts @@ -33,7 +33,7 @@ export const desktop: Messages["desktop"] = { emptyTitle: (bot) => `${bot} ainda não usou seu desktop`, emptyBody: "Quando ele ler ou usar uma janela de um app que você liberou, a janela aparece aqui, ao vivo.", - shortcut: "Ctrl+Alt+Esc para todos os bots no seu desktop, mesmo com o Botloft fechado.", + shortcut: "Ctrl+Alt+Esc para todos os agentes no seu desktop, mesmo com o Botloft fechado.", waiting: "Esperando a imagem…", read: "Leu a janela", click: (target) => `Clicou em "${target}"`, @@ -62,7 +62,7 @@ export const desktop: Messages["desktop"] = { realPoints: (bot) => [ `Quando a acessibilidade não alcança algo nesse app, ${bot} move o seu cursor e digita como se fosse você.`, `Enquanto ele age, o mouse e o teclado são dele, e um aviso na sua tela diz isso.`, - `Mexa o mouse ou aperte uma tecla e ele para na hora. Ctrl+Alt+Esc para todos os bots no seu desktop.`, + `Mexa o mouse ou aperte uma tecla e ele para na hora. Ctrl+Alt+Esc para todos os agentes no seu desktop.`, `Ele nunca digita em campos de senha e nunca aperta teclas do próprio Windows, como a tecla Windows ou Alt+Tab.`, ], realConfirm: "Ligar", diff --git a/app/src/i18n/pt-BR/messages.ts b/app/src/i18n/pt-BR/messages.ts index 726ecf4b..ce53a918 100644 --- a/app/src/i18n/pt-BR/messages.ts +++ b/app/src/i18n/pt-BR/messages.ts @@ -2,7 +2,7 @@ import type { Messages } from "../en"; export const messages: Messages["messages"] = { calls: { - label: "Bots chamando uns aos outros", + label: "Agentes chamando uns aos outros", caller: (bot) => `${bot}:`, calling: (bot) => `Chamando ${bot}`, picked: (bot) => `${bot} atendeu`, @@ -11,15 +11,15 @@ export const messages: Messages["messages"] = { messageTo: "Mensagem para", recipient: "Destinatário", placeholder: (handle: string | undefined) => - `Escreva para @${handle ?? "bot"}. Ctrl+Enter envia.`, + `Escreva para @${handle ?? "agente"}. Ctrl+Enter envia.`, send: "Enviar", }, delivery: { delivered: "Entregue", read: "Lida", - readTitle: "O bot começou a trabalhar nela", + readTitle: "O agente começou a trabalhar nela", delivering: "Entregando", - waiting: "Aguardando o bot", + waiting: "Aguardando o agente", retrying: (when: string, attempts: number) => `Nova tentativa ${when}, após ${attempts} ${attempts === 1 ? "tentativa sem sucesso" : "tentativas sem sucesso"}`, notDelivered: "Não entregue", @@ -31,14 +31,14 @@ export const messages: Messages["messages"] = { title: "Mensagens não entregues", retryAll: "Tentar todas de novo", explanation: - "O Botloft desistiu após várias tentativas. Tentar de novo coloca a mensagem de volta na fila; ela é enviada quando o bot estiver pronto.", + "O Botloft desistiu após várias tentativas. Tentar de novo coloca a mensagem de volta na fila; ela é enviada quando o agente estiver pronto.", to: (name: string) => `Para ${name}`, tries: (count: number) => `${count} ${count === 1 ? "tentativa" : "tentativas"}`, }, row: { you: "Você", system: "Botloft", - goneBot: "um bot que não está mais na equipe", + goneBot: "um agente que não está mais na equipe", to: "para", task: "Tarefa", result: "Resultado", diff --git a/app/src/i18n/pt-BR/onboarding.ts b/app/src/i18n/pt-BR/onboarding.ts index d69fe2b0..cd97108e 100644 --- a/app/src/i18n/pt-BR/onboarding.ts +++ b/app/src/i18n/pt-BR/onboarding.ts @@ -9,18 +9,18 @@ export const onboarding: Messages["onboarding"] = { install: "Preparando o Botloft…", update: "Atualizando o Botloft…", restart: "Reiniciando o Botloft…", - start: "Iniciando seus bots…", + start: "Iniciando seus agentes…", }, installNote: (system: string) => - `O Botloft roda seus bots em segundo plano. Em Configurações você escolhe se eles continuam trabalhando depois que você fecha esta janela e se o Botloft inicia com o ${system}.`, + `O Botloft roda seus agentes em segundo plano. Em Configurações você escolhe se eles continuam trabalhando depois que você fecha esta janela e se o Botloft inicia com o ${system}.`, stopped: { title: "O Botloft não conseguiu iniciar", - body: "O Botloft roda seus bots em segundo plano, e essa parte não iniciou.", + body: "O Botloft roda seus agentes em segundo plano, e essa parte não iniciou.", notRunning: "Não está rodando.", }, outdated: { title: "O Botloft não conseguiu terminar a atualização", - body: "A parte que roda seus bots em segundo plano continua na versão anterior.", + body: "A parte que roda seus agentes em segundo plano continua na versão anterior.", running: (version: string) => `Versão em execução: ${version}.`, stillOld: (version: string) => `Ainda informa a versão ${version}.`, }, @@ -45,33 +45,34 @@ export const onboarding: Messages["onboarding"] = { welcome: { title: "Boas-vindas ao Botloft", intro: - "Uma equipe é um grupo de bots que ficam sempre ligados, trocam mensagens e dividem uma pasta. Cada bot roda no agente que você escolher: Claude Code, ou outro que você ligar. Toda equipe começa com um chefe: diga para que ela serve, e ele planeja o trabalho e sugere os bots de que precisa.", + "Uma equipe é um grupo de agentes que ficam sempre ligados, trocam mensagens e dividem uma pasta. Cada agente roda no motor que você escolher: Claude Code, ou outro que você ligar. Toda equipe começa com um chefe: diga para que ela serve, e ele planeja o trabalho e sugere os agentes de que precisa.", botloft: "Botloft", running: (system: string) => - `Rodando em segundo plano. Inicia com o ${system}, então seus bots continuam trabalhando depois que você fecha esta janela.`, + `Rodando em segundo plano. Inicia com o ${system}, então seus agentes continuam trabalhando depois que você fecha esta janela.`, runningNotAtStart: - "Rodando em segundo plano, então seus bots continuam trabalhando depois que você fecha esta janela.", - runningWhileOpen: "Rodando enquanto esta janela está aberta. Ao fechá-la, seus bots param.", + "Rodando em segundo plano, então seus agentes continuam trabalhando depois que você fecha esta janela.", + runningWhileOpen: "Rodando enquanto esta janela está aberta. Ao fechá-la, seus agentes param.", claudeCode: "Claude Code", checking: "Verificando…", version: (version: string) => `Versão ${version}`, account: "Conta do Claude", signedIn: "Conectado.", - signedOut: "Seus bots trabalham com a sua conta do Claude. Entre uma vez e eles ficam prontos.", + signedOut: + "Seus agentes trabalham com a sua conta do Claude. Entre uma vez e eles ficam prontos.", ready: "pronto", notReady: "não está pronto", stillChecking: "verificando", - askFirstTitle: "Os bots perguntam antes de mudar as coisas", + askFirstTitle: "Os agentes perguntam antes de mudar as coisas", askFirstBody: - "Quando um bot quer rodar um comando ou editar um arquivo, ele pergunta no chat e espera você permitir ou negar.", + "Quando um agente quer rodar um comando ou editar um arquivo, ele pergunta no chat e espera você permitir ou negar.", ideasTitle: "Ou toque em uma ideia", - claudeOptional: "Não instalado. Você só precisa dele para os bots que rodam no Claude Code.", + claudeOptional: "Não instalado. Você só precisa dele para os agentes que rodam no Claude Code.", agentMissing: "Não encontrado. Instale e abra o Botloft de novo.", notNeeded: "não precisa", createCrew: "Criar sua primeira equipe", }, claudeCode: { - help: "O Botloft roda seus bots com o Claude Code. Instale ou atualize, abra uma vez para entrar na sua conta, e o Botloft percebe em até 30 segundos.", + help: "O Botloft roda seus agentes com o Claude Code. Instale ou atualize, abra uma vez para entrar na sua conta, e o Botloft percebe em até 30 segundos.", install: "Como instalar o Claude Code", openFailed: "Não foi possível abrir o link", }, diff --git a/app/src/i18n/pt-BR/phone.ts b/app/src/i18n/pt-BR/phone.ts index 3a1e5513..3f04bf3d 100644 --- a/app/src/i18n/pt-BR/phone.ts +++ b/app/src/i18n/pt-BR/phone.ts @@ -6,7 +6,7 @@ export const phone: Messages["phone"] = { pair: { title: "Conectar este celular", intro: - "Este celular vai aprovar os pedidos dos seus bots responder às perguntas deles e conversar com eles, de qualquer lugar. Os arquivos ficam no seu computador.", + "Este celular vai aprovar os pedidos dos seus agentes responder às perguntas deles e conversar com eles, de qualquer lugar. Os arquivos ficam no seu computador.", nameLabel: "Nome deste celular", connect: "Conectar", connecting: "Conectando…", @@ -23,7 +23,7 @@ export const phone: Messages["phone"] = { inbox: { title: "Esperando você", empty: "Nada esperando você", - emptyBody: "Quando um bot precisar de você, aparece aqui.", + emptyBody: "Quando um agente precisar de você, aparece aqui.", loading: "Vendo o que está esperando…", computerOff: "O computador está desligado ou sem internet. Os pedidos continuam esperando lá.", noConnection: "Sem conexão. Tentando de novo…", @@ -32,9 +32,9 @@ export const phone: Messages["phone"] = { }, tabs: { requests: "Pedidos", chats: "Conversas", newReply: "resposta nova" }, chats: { - empty: "Nenhum bot ainda", - emptyBody: "Quando você criar um bot no computador, ele aparece aqui.", - loading: "Carregando os bots…", + empty: "Nenhum agente ainda", + emptyBody: "Quando você criar um agente no computador, ele aparece aqui.", + loading: "Carregando os agentes…", working: "trabalhando…", kind: { owner: "Você", @@ -48,7 +48,7 @@ export const phone: Messages["phone"] = { inCrew: (crew: string) => `em ${crew}`, allCrews: "Todas", crewsLabel: "Equipes", - bots: (n: number) => `${n} ${n === 1 ? "bot" : "bots"}`, + bots: (n: number) => `${n} ${n === 1 ? "agente" : "agentes"}`, back: "Conversas", loadingChat: "Carregando a conversa…", nothing: "Nada aqui ainda. Escreva a primeira mensagem.", @@ -69,19 +69,19 @@ export const phone: Messages["phone"] = { }, pending: { sending: "Enviando…", - sent: "Enviada. O bot vai ver quando puder.", + sent: "Enviada. O agente vai ver quando puder.", notSent: "Não enviada.", reasons: { offline: "Veja a conexão e tente de novo.", timeout: "O computador não respondeu. Veja se está ligado e tente de novo.", - invalid: "O bot não aceita este texto.", - not_found: "Este bot não existe mais.", + invalid: "O agente não aceita este texto.", + not_found: "Este agente não existe mais.", rate_limited: "Mensagens demais em um minuto. Espere um pouco.", failed: "Algo deu errado. Tente de novo.", } as Record, }, reach: - "Um celular conectado e desbloqueado pode ler as conversas dos seus bots e escrever para eles.", + "Um celular conectado e desbloqueado pode ler as conversas dos seus agentes e escrever para eles.", }, approval: { asks: (bot: string) => `${bot} pede para`, @@ -92,7 +92,7 @@ export const phone: Messages["phone"] = { allow: "Permitir", deny: "Negar", sending: "Enviando…", - noteLabel: "Recado para o bot (opcional)", + noteLabel: "Recado para o agente (opcional)", cut: "Longo demais para ler no celular. Aqui você só pode negar, ou responder no computador.", atComputer: "Este pedido precisa de você no computador. Aqui você só pode negar.", failed: "Não foi enviado. Confira a conexão e tente de novo.", @@ -114,7 +114,7 @@ export const phone: Messages["phone"] = { notices: { turnOn: "Ligar os avisos", turnOff: "Desligar os avisos", - on: "Os avisos estão ligados. Você recebe um quando um bot precisar de você.", + on: "Os avisos estão ligados. Você recebe um quando um agente precisar de você.", off: "Os avisos estão desligados.", blocked: "Os avisos estão bloqueados para esta página. Libere nas configurações do navegador.", diff --git a/app/src/i18n/pt-BR/questions.ts b/app/src/i18n/pt-BR/questions.ts index b486c0ec..57579ff4 100644 --- a/app/src/i18n/pt-BR/questions.ts +++ b/app/src/i18n/pt-BR/questions.ts @@ -20,16 +20,16 @@ export const questions: Messages["questions"] = { label: "Perguntas", open: (count: number) => count === 0 - ? "Perguntas dos seus bots" + ? "Perguntas dos seus agentes" : count === 1 ? "1 pergunta espera você" : `${count} perguntas esperam você`, - title: "Perguntas dos seus bots", + title: "Perguntas dos seus agentes", intro: - "Quando um bot precisa de uma decisão sua para continuar, ele pergunta aqui e segue o trabalho. Sua resposta chega a ele como mensagem.", + "Quando um agente precisa de uma decisão sua para continuar, ele pergunta aqui e segue o trabalho. Sua resposta chega a ele como mensagem.", emptyTitle: "Nenhuma pergunta esperando você", emptyBody: - "Os bots perguntam aqui quando precisam de você, até de madrugada, numa rotina. Responda quando puder.", + "Os agentes perguntam aqui quando precisam de você, até de madrugada, numa rotina. Responda quando puder.", openChat: (bot: string) => `Abrir a conversa com ${bot}`, inCrew: (crew: string) => `em ${crew}`, }, diff --git a/app/src/i18n/pt-BR/routines.ts b/app/src/i18n/pt-BR/routines.ts index fbb4e9e8..f25df7fa 100644 --- a/app/src/i18n/pt-BR/routines.ts +++ b/app/src/i18n/pt-BR/routines.ts @@ -8,7 +8,7 @@ export const routines: Messages["routines"] = { empty: (bot: string) => `${bot} ainda não tem rotinas.`, emptyHint: (bot: string) => `Uma rotina faz ${bot} trabalhar sozinho em horários marcados, como "dias úteis às 09:00", e o resultado aparece no chat.`, - crewEmpty: "Nenhum bot desta equipe tem rotina ainda. Abra um bot para criar uma.", + crewEmpty: "Nenhum agente desta equipe tem rotina ainda. Abra um agente para criar uma.", newRoutine: "Nova rotina", toggle: (name: string) => `${name} ligada ou desligada`, runNow: "Rodar agora", @@ -26,7 +26,7 @@ export const routines: Messages["routines"] = { done: "A última rodou bem", failed: "A última falhou", overlap: "A última vez foi pulada: a anterior ainda não tinha terminado", - bot_paused: "A última vez foi pulada: o bot estava pausado", + bot_paused: "A última vez foi pulada: o agente estava pausado", too_soon: "O último aviso foi pulado: chegou menos de 5 minutos depois do anterior", signalFrom: (bot: string) => `aviso de ${bot}`, missed: (count: number) => @@ -36,7 +36,7 @@ export const routines: Messages["routines"] = { }, page: { title: "Rotinas", - lead: "O que seus bots fazem sozinhos, em todas as equipes.", + lead: "O que seus agentes fazem sozinhos, em todas as equipes.", upcoming: "Próximas", watching: "Vigiando", showMore: (count) => `Mostrar mais ${count}`, @@ -44,7 +44,7 @@ export const routines: Messages["routines"] = { of: (bot) => `Rotinas de ${bot}`, emptyTitle: "Nenhuma rotina ainda", emptyBody: - "Uma rotina faz um bot trabalhar sozinho em horários marcados. Peça a um bot no chat, ou abra a aba Rotinas de um bot.", + "Uma rotina faz um agente trabalhar sozinho em horários marcados. Peça a um agente no chat, ou abra a aba Rotinas de um agente.", }, when: { everyDay: (time: string) => `Todo dia às ${time}`, @@ -54,7 +54,7 @@ export const routines: Messages["routines"] = { everyMinutes: (count: number) => (count === 1 ? "A cada minuto" : `A cada ${count} minutos`), everyHours: (count: number) => (count === 1 ? "A cada hora" : `A cada ${count} horas`), cron: (expr: string) => `Cron: ${expr}`, - signal: (name: string) => `Quando um bot avisar “${name}”`, + signal: (name: string) => `Quando um agente avisar “${name}”`, }, tag: (name: string) => `Rotina · ${name}`, dialog: { @@ -70,7 +70,7 @@ export const routines: Messages["routines"] = { weekdays: "Dias úteis", days: "Dias escolhidos", interval: "A cada…", - signal: "Quando um bot avisar", + signal: "Quando um agente avisar", }, at: "Às", every: "A cada", @@ -90,7 +90,7 @@ export const routines: Messages["routines"] = { signal: "Aviso", signalPlaceholder: "relatorio-pronto", signalHint: (bot: string) => - `${bot} faz isto quando um bot da equipe mandar esse aviso. Diga a esse bot, nas instruções dele, quando avisar, como “avise relatorio-pronto quando salvar o relatório”.`, + `${bot} faz isto quando um agente da equipe mandar esse aviso. Diga a esse agente, nas instruções dele, quando avisar, como “avise relatorio-pronto quando salvar o relatório”.`, cronHint: "Minuto, hora, dia do mês, mês e dia da semana. Ela substitui o Quando.", create: "Criar rotina", save: "Salvar", @@ -108,10 +108,10 @@ export const routines: Messages["routines"] = { }, summary: { title: "Resumo diário", - hint: "Todo dia o chefe conta o que cada bot fez, o que está parado e o que está esperando por você.", + hint: "Todo dia o chefe conta o que cada agente fez, o que está parado e o que está esperando por você.", name: "Resumo diário", prompt: - "O dia está acabando. Use crew_activity (as últimas 24 horas) e escreva para mim um resumo curto do dia: o que cada bot terminou, o que ainda está aberto ou atrasado, o que deu errado e o que está esperando por mim (aprovações e perguntas), o mais importante primeiro. Palavras simples, no máximo 12 linhas. Se nada aconteceu, diga isso em uma linha. Não comece trabalho novo e não escreva para outros bots por causa disso.", + "O dia está acabando. Use crew_activity (as últimas 24 horas) e escreva para mim um resumo curto do dia: o que cada agente terminou, o que ainda está aberto ou atrasado, o que deu errado e o que está esperando por mim (aprovações e perguntas), o mais importante primeiro. Palavras simples, no máximo 12 linhas. Se nada aconteceu, diga isso em uma linha. Não comece trabalho novo e não escreva para outros agentes por causa disso.", time: "Horário", turnOn: "Ligar", turnOff: "Desligar", diff --git a/app/src/i18n/pt-BR/search.ts b/app/src/i18n/pt-BR/search.ts index 8108b492..9c1f1f74 100644 --- a/app/src/i18n/pt-BR/search.ts +++ b/app/src/i18n/pt-BR/search.ts @@ -9,7 +9,7 @@ export const search: Messages["search"] = { placeholder: "Procure palavras nas suas conversas", scope: "Onde buscar", allCrews: "Todas as equipes", - hint: "Acha palavras no que você, outros bots e o Botloft escreveram para seus bots, no que eles responderam e nas perguntas deles.", + hint: "Acha palavras no que você, outros agentes e o Botloft escreveram para seus agentes, no que eles responderam e nas perguntas deles.", tooShort: "Digite pelo menos 2 letras", results: "Resultados", nothing: (query: string) => `Nada encontrado para “${query}”`, @@ -19,6 +19,6 @@ export const search: Messages["search"] = { inCrew: (crew: string) => `em ${crew}`, you: "Você", botloft: "Botloft", - goneBot: "Um bot que não está mais na equipe", + goneBot: "Um agente que não está mais na equipe", openHere: (bot: string) => `Abrir a conversa com ${bot} neste ponto`, }; diff --git a/app/src/i18n/pt-BR/setup.ts b/app/src/i18n/pt-BR/setup.ts index b3539d64..a030630c 100644 --- a/app/src/i18n/pt-BR/setup.ts +++ b/app/src/i18n/pt-BR/setup.ts @@ -1,7 +1,7 @@ import type { Messages } from "../en"; export const setup: Messages["setup"] = { - tagline: "Bots sempre ligados, trabalhando juntos para você.", + tagline: "Agentes sempre ligados, trabalhando juntos para você.", install: "Instalar", update: "Atualizar", forYou: "Instala só para você, sem pedir administrador.", @@ -10,7 +10,7 @@ export const setup: Messages["setup"] = { newer: (installed: string) => `Você já tem uma versão mais nova (${installed}).`, open: "Abrir o Botloft", reinstall: "Instalar de novo", - appOpen: "O Botloft está aberto e fecha para instalar. Seus bots continuam trabalhando.", + appOpen: "O Botloft está aberto e fecha para instalar. Seus agentes continuam trabalhando.", installing: "Instalando o Botloft…", installingLine: "Leva só alguns segundos.", progress: "Instalando", diff --git a/app/src/i18n/pt-BR/shell.ts b/app/src/i18n/pt-BR/shell.ts index ab009a07..7f4af376 100644 --- a/app/src/i18n/pt-BR/shell.ts +++ b/app/src/i18n/pt-BR/shell.ts @@ -13,8 +13,8 @@ export const shell: Messages["shell"] = { home: "Início", }, sidebar: { - hide: "Esconder a lista de bots", - show: "Mostrar a lista de bots", + hide: "Esconder a lista de agentes", + show: "Mostrar a lista de agentes", }, zoom: { level: (percent: number, isDefault: boolean) => @@ -30,9 +30,9 @@ export const shell: Messages["shell"] = { }, loadFailed: "Não foi possível carregar suas equipes", recycleFailed: (path: string) => `A pasta ${path} não foi para a Lixeira e continua lá`, - botsCantStart: "Os bots não conseguem iniciar", + botsCantStart: "Os agentes não conseguem iniciar", signIn: { title: "Entre no Claude", - body: "Seus bots trabalham com a sua conta do Claude. Entre uma vez e eles começam sozinhos.", + body: "Seus agentes trabalham com a sua conta do Claude. Entre uma vez e eles começam sozinhos.", }, }; diff --git a/app/src/i18n/pt-BR/tools.ts b/app/src/i18n/pt-BR/tools.ts index 333da8ef..9f37b202 100644 --- a/app/src/i18n/pt-BR/tools.ts +++ b/app/src/i18n/pt-BR/tools.ts @@ -31,12 +31,12 @@ export const tools: Messages["tools"] = { send_message: "Mandar uma mensagem", complete_task: "Concluir uma tarefa", my_tasks: "Ver as próprias tarefas", - suggest_bot: "Sugerir um bot", + suggest_bot: "Sugerir um agente", schedule_routine: "Criar uma rotina", my_routines: "Ver minhas rotinas", change_routine: "Mudar uma rotina", delete_routine: "Apagar uma rotina", - change_bot: "Mudar um bot", + change_bot: "Mudar um agente", ask_crew_access: "Acessar outra equipe", share_file: "Compartilhar um arquivo", ask_owner: "Fazer uma pergunta a você", diff --git a/app/src/i18n/pt-BR/updates.ts b/app/src/i18n/pt-BR/updates.ts index aeb36acc..665ad6b8 100644 --- a/app/src/i18n/pt-BR/updates.ts +++ b/app/src/i18n/pt-BR/updates.ts @@ -4,7 +4,7 @@ export const updates: Messages["updates"] = { available: "Atualização disponível", title: "Atualizar o Botloft", ready: (version: string) => - `O Botloft ${version} está pronto. O Botloft fecha, instala a atualização e abre de novo. Seus bots pausam por um instante e continuam de onde pararam.`, + `O Botloft ${version} está pronto. O Botloft fecha, instala a atualização e abre de novo. Seus agentes pausam por um instante e continuam de onde pararam.`, whatsNew: "Novidades", later: "Depois", updateNow: "Atualizar agora", diff --git a/app/src/lib/fakeCatalog.test.ts b/app/src/lib/fakeCatalog.test.ts index fff54382..826bf838 100644 --- a/app/src/lib/fakeCatalog.test.ts +++ b/app/src/lib/fakeCatalog.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; import { FakeBotloft } from "./fake"; -describe("the fake's bot catalog", () => { +describe("the fake's agent catalog", () => { it("lists the roles, by category too, and reads one with its instructions", async () => { const fake = new FakeBotloft(); expect(await fake.call("catalog.list", {})).toHaveLength(129); @@ -31,7 +31,7 @@ describe("the fake's bot catalog", () => { await expect(fake.call("catalog.get", { id: "wizard" })).rejects.toThrow(); }); - it("adds a role as a plain bot, with the name and role the app wrote", async () => { + it("adds a role as a plain agent, with the name and role the app wrote", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Studio"); const seen: string[] = []; @@ -41,7 +41,7 @@ describe("the fake's bot catalog", () => { crewId: crew.id, templateId: "code-reviewer", name: "Revisor de código", - role: "Revisa o que os outros bots mudaram", + role: "Revisa o que os outros agents mudaram", }); const sheet = await fake.call("catalog.get", { id: "code-reviewer" }); expect(bot.name).toBe("Revisor de código"); diff --git a/app/src/lib/fakeMcp.test.ts b/app/src/lib/fakeMcp.test.ts index d5825285..e8743ef2 100644 --- a/app/src/lib/fakeMcp.test.ts +++ b/app/src/lib/fakeMcp.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest"; import { FakeBotloft } from "./fake"; describe("the fake's connected tools", () => { - it("registers a tool, attaches it to a bot and lets it go with the tool", async () => { + it("registers a tool, attaches it to an agent and lets it go with the tool", async () => { const fake = new FakeBotloft(); const seen: string[] = []; fake.subscribe((event) => seen.push(event.name)); diff --git a/app/src/lib/tauriTray.test.ts b/app/src/lib/tauriTray.test.ts index ff422127..81663fc0 100644 --- a/app/src/lib/tauriTray.test.ts +++ b/app/src/lib/tauriTray.test.ts @@ -31,8 +31,8 @@ vi.mock("@tauri-apps/api/tray", () => ({ })); const view: TrayView = { - status: "No bot working", - tooltip: "Botloft: No bot working", + status: "No agent working", + tooltip: "Botloft: No agent working", open: "Open", pause: "Pause", quit: "Quit", diff --git a/app/src/phone/chats.test.tsx b/app/src/phone/chats.test.tsx index 562c4b57..9de6f6af 100644 --- a/app/src/phone/chats.test.tsx +++ b/app/src/phone/chats.test.tsx @@ -52,7 +52,7 @@ async function openChat(phone: FakePhone, items: PhoneItem[], more = false) { } describe("the list of conversations", () => { - test("shows each bot with its last words, working, and a dot for a reply not seen", () => { + test("shows each agent with its last words, working, and a dot for a reply not seen", () => { const phone = new FakePhone(); phone.receive({ t: "chats", @@ -85,12 +85,12 @@ describe("the list of conversations", () => { const phone = new FakePhone(); render(); fireEvent.click(screen.getByRole("tab", { name: /Chats/ })); - expect(screen.getByText("Loading the bots…")).toBeTruthy(); + expect(screen.getByText("Loading the agents…")).toBeTruthy(); act(() => phone.receive({ t: "chats", bots: [], first: true })); - expect(screen.getByText("No bots yet")).toBeTruthy(); + expect(screen.getByText("No agents yet")).toBeTruthy(); }); - test("a line that changes moves the bot up", () => { + test("a line that changes moves the agent up", () => { const quiet = line({ botId: "bot_2", name: "Writer" }); delete quiet.last; const phone = withList(line(), quiet); @@ -108,7 +108,7 @@ describe("the list of conversations", () => { expect(names[0]).toContain("Writer"); }); - test("the number kept for each bot is a time and never a text", async () => { + test("the number kept for each agent is a time and never a text", async () => { const kept: Record[] = []; const store: SeenStore = { load: () => ({}), save: (seen) => kept.push(seen) }; const phone = new FakePhone(store); @@ -121,7 +121,7 @@ describe("the list of conversations", () => { }); describe("a conversation", () => { - test("opens with the newest items, asks to be told what happens and says what the bot does", async () => { + test("opens with the newest items, asks to be told what happens and says what the agent does", async () => { const phone = withList(line({ state: "busy" })); await openChat(phone, [ you("itm_1", "Hello"), @@ -255,7 +255,7 @@ describe("a conversation", () => { }); }); -describe("writing to a bot", () => { +describe("writing to an agent", () => { const box = () => screen.getByLabelText("Message to Scout") as HTMLTextAreaElement; const write = (value: string) => fireEvent.change(box(), { target: { value } }); const sendButton = () => screen.getByRole("button", { name: "Send" }) as HTMLButtonElement; @@ -274,7 +274,7 @@ describe("writing to a bot", () => { const clientId = asked?.t === "send" ? asked.clientId : ""; act(() => phone.receive({ t: "sent", clientId, ok: true })); - expect(screen.getByText("Sent. The bot will see it when it can.")).toBeTruthy(); + expect(screen.getByText("Sent. The agent will see it when it can.")).toBeTruthy(); act(() => phone.receive({ t: "item", botId: "bot_1", item: you("itm_9", "Do the report", 9) })); // One bubble only: the pending one is gone. expect(screen.getAllByText("Do the report")).toHaveLength(1); @@ -318,7 +318,7 @@ describe("finding the way around", () => { line({ botId: "bot_3", name: "Editor", crew: "Marketing", lastReplyAt: 1 }), ]; - test("groups the bots by crew, and the chips narrow the list to one", () => { + test("groups the agents by crew, and the chips narrow the list to one", () => { const phone = withList(...many()); act(() => phone.set({ seen: { bot_1: 100, bot_2: 1, bot_3: 1 } })); const sections = screen.getAllByRole("region"); @@ -326,7 +326,7 @@ describe("finding the way around", () => { "Marketing", "Ops", ]); - expect(within(sections[0] as HTMLElement).getByText("2 bots")).toBeTruthy(); + expect(within(sections[0] as HTMLElement).getByText("2 agents")).toBeTruthy(); fireEvent.click(screen.getByRole("button", { name: /^Ops/ })); expect(screen.queryByRole("button", { name: /Writer/ })).toBeNull(); expect(screen.getByRole("button", { name: /Scout/ })).toBeTruthy(); diff --git a/app/src/phone/phone.test.tsx b/app/src/phone/phone.test.tsx index d47ffe97..ef39a702 100644 --- a/app/src/phone/phone.test.tsx +++ b/app/src/phone/phone.test.tsx @@ -43,7 +43,7 @@ describe("what waits for the owner", () => { expect(screen.getByText("Nothing is waiting for you")).toBeTruthy(); }); - test("shows a request with what the bot says it is for, and the whole of it a tap away", async () => { + test("shows a request with what the agent says it is for, and the whole of it a tap away", async () => { const phone = new FakePhone(); phone.add(approval()); render(); @@ -56,7 +56,7 @@ describe("what waits for the owner", () => { expect(screen.getByText("git status --short")).toBeTruthy(); }); - test("warns when the bot did not say what a command is for", () => { + test("warns when the agent did not say what a command is for", () => { const phone = new FakePhone(); const { explanation: _unsaid, ...unexplained } = approval(); phone.add(unexplained); @@ -68,7 +68,7 @@ describe("what waits for the owner", () => { const phone = new FakePhone(); phone.add(approval()); render(); - fireEvent.change(screen.getByLabelText("Note for the bot (optional)"), { + fireEvent.change(screen.getByLabelText("Note for the agent (optional)"), { target: { value: "go on" }, }); fireEvent.click(screen.getByRole("button", { name: "Allow" })); diff --git a/app/src/setup/setup.test.tsx b/app/src/setup/setup.test.tsx index 9753d85c..8386fdd1 100644 --- a/app/src/setup/setup.test.tsx +++ b/app/src/setup/setup.test.tsx @@ -57,9 +57,9 @@ describe("setup window", () => { expect(host.calls).not.toContain("install"); }); - test("says the open app closes and the bots keep working", async () => { + test("says the open app closes and the agents keep working", async () => { await open({ relation: "older", appRunning: true }); - expect(screen.getByText(/closes to install\. Your bots keep working/)).toBeDefined(); + expect(screen.getByText(/closes to install\. Your agents keep working/)).toBeDefined(); }); test("a failed install offers to retry or the classic installer", async () => { diff --git a/app/src/shell/alerts.test.tsx b/app/src/shell/alerts.test.tsx index eb7ba40d..361b7394 100644 --- a/app/src/shell/alerts.test.tsx +++ b/app/src/shell/alerts.test.tsx @@ -25,7 +25,7 @@ async function scoutBehind() { } describe("notifications", () => { - test("say which bot needs the owner and for what, with the Windows sound", async () => { + test("say which agent needs the owner and for what, with the Windows sound", async () => { const { fake, host, scout } = await scoutBehind(); act(() => { fake.chat.ask(scout.id, "Bash", "npm test", '{"command":"npm test"}'); @@ -42,7 +42,7 @@ describe("notifications", () => { }); }); - test("tell when a bot finishes only if the owner wants it, and stay quiet without sound", async () => { + test("tell when an agent finishes only if the owner wants it, and stay quiet without sound", async () => { const { fake, host, scout } = await scoutBehind(); act(() => fake.setBotState(scout.id, "busy")); act(() => fake.setBotState(scout.id, "idle")); @@ -70,7 +70,7 @@ describe("notifications", () => { expect(host.notices).toEqual([]); }); - test("opening Botloft from one goes to its bot", async () => { + test("opening Botloft from one goes to its agent", async () => { const { fake, host, writer } = await scoutBehind(); act(() => fake.setBotState(writer.id, "needs_approval")); expect(host.notices).toHaveLength(1); diff --git a/app/src/shell/rail.test.tsx b/app/src/shell/rail.test.tsx index 29867895..6e83b034 100644 --- a/app/src/shell/rail.test.tsx +++ b/app/src/shell/rail.test.tsx @@ -36,7 +36,7 @@ describe("the rail", () => { const scout = fake.addBot(fake.addCrew("Ops").id, "Scout"); renderApp(fake); await crewOpened("Ops"); - fireEvent.click(screen.getByRole("button", { name: "Hide the bots list" })); + fireEvent.click(screen.getByRole("button", { name: "Hide the agents list" })); expect(slot().dataset.open).toBe("false"); act(() => { fake.questions.ask(scout.id, "Which client first?"); diff --git a/app/src/shell/sidebarToggle.test.tsx b/app/src/shell/sidebarToggle.test.tsx index bcabbf8a..0edc6e9d 100644 --- a/app/src/shell/sidebarToggle.test.tsx +++ b/app/src/shell/sidebarToggle.test.tsx @@ -21,15 +21,15 @@ async function oneCrew() { return { fake, scout }; } -describe("hiding the bots list", () => { +describe("hiding the agents list", () => { test("the title bar button hides the list and brings it back, and it is remembered", async () => { await oneCrew(); expect(slot().dataset.open).toBe("true"); - fireEvent.click(screen.getByRole("button", { name: "Hide the bots list" })); + fireEvent.click(screen.getByRole("button", { name: "Hide the agents list" })); expect(slot().dataset.open).toBe("false"); expect(slot().hasAttribute("inert")).toBe(true); expect(localStorage.getItem("botloft.sidebar")).toBe("false"); - fireEvent.click(screen.getByRole("button", { name: "Show the bots list" })); + fireEvent.click(screen.getByRole("button", { name: "Show the agents list" })); expect(slot().dataset.open).toBe("true"); expect(slot().hasAttribute("inert")).toBe(false); }); @@ -44,10 +44,10 @@ describe("hiding the bots list", () => { expect(slot().dataset.open).toBe("true"); }); - test("with the list hidden, a dot on the button says a bot is waiting", async () => { + test("with the list hidden, a dot on the button says an agent is waiting", async () => { const { fake, scout } = await oneCrew(); - fireEvent.click(screen.getByRole("button", { name: "Hide the bots list" })); - const button = screen.getByRole("button", { name: "Show the bots list" }); + fireEvent.click(screen.getByRole("button", { name: "Hide the agents list" })); + const button = screen.getByRole("button", { name: "Show the agents list" }); expect(button.parentElement?.querySelector(".live-dot")).toBeNull(); fake.setBotState(scout.id, "needs_approval"); await waitFor(() => expect(button.parentElement?.querySelector(".live-dot")).not.toBeNull()); diff --git a/app/src/shell/sounds.test.tsx b/app/src/shell/sounds.test.tsx index aa9a8788..33b52613 100644 --- a/app/src/shell/sounds.test.tsx +++ b/app/src/shell/sounds.test.tsx @@ -81,7 +81,7 @@ describe("app sounds", () => { expect(played()).toEqual(["reply", "file"]); }); - test("a spark for a new bot, and nothing while Botloft is behind", async () => { + test("a spark for a new agent, and nothing while Botloft is behind", async () => { const { fake, host, ops } = await scoutOpen(); act(() => { fake.addBot(ops.id, "Clerk", "Files things"); @@ -95,7 +95,7 @@ describe("app sounds", () => { expect(played()).toEqual(["spark"]); }); - test("never two within a moment, except a bot that needs the owner", () => { + test("never two within a moment, except an agent that needs the owner", () => { playSound("reply"); playSound("file"); playSound("needs"); diff --git a/app/src/shell/tray.test.tsx b/app/src/shell/tray.test.tsx index 7d145b79..b0cd9438 100644 --- a/app/src/shell/tray.test.tsx +++ b/app/src/shell/tray.test.tsx @@ -22,20 +22,20 @@ function crews() { const view = (host: FakeHost) => host.tray?.view; describe("the icon near the clock", () => { - test("says what the bots are doing, with a dot when something waits", async () => { + test("says what the agents are doing, with a dot when something waits", async () => { const { fake, scout } = crews(); const { host } = renderApp(fake); await crewOpened("Ops"); - await waitFor(() => expect(view(host)?.status).toBe("No bot working")); + await waitFor(() => expect(view(host)?.status).toBe("No agent working")); expect(view(host)).toMatchObject({ - tooltip: "Botloft: No bot working", + tooltip: "Botloft: No agent working", attention: false, open: "Open Botloft", pause: "Pause every crew", quit: "Quit Botloft", }); act(() => fake.setBotState(scout.id, "busy")); - expect(view(host)?.status).toBe("1 bot working"); + expect(view(host)?.status).toBe("1 agent working"); act(() => fake.setBotState(scout.id, "needs_approval")); expect(view(host)).toMatchObject({ status: "Scout needs you", attention: true }); }); @@ -89,7 +89,7 @@ describe("opening at sign-in", () => { expect(host.openAtSignIn).toBe(true); }); - test("does not open the app when the bots do not start with Windows", async () => { + test("does not open the app when the agents do not start with Windows", async () => { const { fake } = crews(); fake.settings = { ...fake.settings, startWithWindows: false }; const { host } = renderApp(fake); diff --git a/app/src/shell/visibility.test.tsx b/app/src/shell/visibility.test.tsx index 6375212b..c5506c89 100644 --- a/app/src/shell/visibility.test.tsx +++ b/app/src/shell/visibility.test.tsx @@ -54,7 +54,7 @@ describe("window visibility", () => { stop(); }); - test("a working bot's files are read over and over only while their panel is in sight", async () => { + test("a working agent's files are read over and over only while their panel is in sight", async () => { vi.useFakeTimers(); const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); diff --git a/app/src/store/app.test.ts b/app/src/store/app.test.ts index 3eacaa29..aa50b9b7 100644 --- a/app/src/store/app.test.ts +++ b/app/src/store/app.test.ts @@ -14,7 +14,7 @@ async function synced(fake: FakeBotloft) { const settle = () => new Promise((resolve) => setTimeout(resolve, 0)); describe("app store", () => { - test("loads crews, bots and the system status, and selects the first crew", async () => { + test("loads crews, agents and the system status, and selects the first crew", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const research = fake.addCrew("Research"); @@ -28,7 +28,7 @@ describe("app store", () => { expect(state.selectedCrewId).toBe(ops.id); }); - test("a chat item moves the bot's list line, not the bot", async () => { + test("a chat item moves the agent's list line, not the agent", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); @@ -64,7 +64,7 @@ describe("app store", () => { expect(store.getState().selectedCrewId).toBeNull(); }); - test("a deleted bot leaves with its routines, tasks and deliveries", async () => { + test("a deleted agent leaves with its routines, tasks and deliveries", async () => { const fake = new FakeBotloft(); const crew = fake.addCrew("Ops"); const scout = fake.addBot(crew.id, "Scout"); @@ -100,7 +100,7 @@ describe("app store", () => { await expect(fake.call("bots.delete", { botId: scout.id })).rejects.toThrow(); }); - test("a deleted crew leaves with its bots, archived or not", async () => { + test("a deleted crew leaves with its agents, archived or not", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); const docs = fake.addCrew("Docs"); diff --git a/app/src/ui/motion.test.tsx b/app/src/ui/motion.test.tsx index 347814d7..cdea08ef 100644 --- a/app/src/ui/motion.test.tsx +++ b/app/src/ui/motion.test.tsx @@ -8,7 +8,7 @@ import { SeenSince, useArrival } from "./motion"; afterEach(cleanup); describe("the mascot's mood", () => { - test("follows what the bot does", () => { + test("follows what the agent does", () => { const bot = (state: Parameters[0]["state"], paused = false) => moodOf({ state, paused }); expect(bot("idle")).toBe("idle"); @@ -62,7 +62,7 @@ describe("arrivals", () => { }); describe("switching views", () => { - test("the new bot's pane rises in, and the window around it stays", async () => { + test("the new agent's pane rises in, and the window around it stays", async () => { const fake = new FakeBotloft(); const ops = fake.addCrew("Ops"); for (const name of ["Scout", "Writer"]) { From d7c7264d4066f439ca654a072bb20812ae8244a6 Mon Sep 17 00:00:00 2001 From: Phelipe Lorran <112786693+httpsphl@users.noreply.github.com> Date: Sat, 10 Oct 2026 19:25:55 -0300 Subject: [PATCH 2/2] docs: describe Botloft as AI agents that stay on and work together The README tagline, intro and Engines section, SECURITY, the daemon's crate description and the design docs no longer say a crew of Claude bots. CLAUDE.md records the word rule: agent for the owner, engine for Claude Code, Antigravity and Codex, bot only in the name. --- .design-sync/conventions.md | 54 ++++++++--------- CLAUDE.md | 3 +- README.md | 116 ++++++++++++++++++------------------ SECURITY.md | 8 +-- crates/botloftd/Cargo.toml | 2 +- 5 files changed, 92 insertions(+), 91 deletions(-) diff --git a/.design-sync/conventions.md b/.design-sync/conventions.md index 2a9c0b65..b8e4a1d7 100644 --- a/.design-sync/conventions.md +++ b/.design-sync/conventions.md @@ -1,6 +1,6 @@ # Building with Botloft -Botloft is a desktop app where a crew of Claude Code bots works together. Each bot is a small flame +Botloft is a desktop app where a crew of AI agents works together. Each agent is a small flame mascot in its own color. The look is quiet and warm: neutral surfaces, one orange accent, IBM Plex. ## Setup @@ -9,9 +9,9 @@ mascot in its own color. The look is quiet and warm: neutral surfaces, one orang (Button, fields, Dialog, Callout, BotAvatar, BotStateBadge...) need nothing around them. - **App screens need `BotloftProvider`.** `Sidebar`, `CrewBots`, `BotRun`, `InboundRow` and `ApprovalCard` are the app's real screens and read the app's data: wrap them in one - `` per design (not one per piece). Build the data - with `makeCrew`, `makeBot`, `makeMessage` and `chat.*` (below); never write a bot object by hand. - A new `bots` array updates every screen, which is how a design animates states. + `` per design (not one per piece). Build the data + with `makeCrew`, `makeBot`, `makeMessage` and `chat.*` (below); never write an agent object by hand. + A new `agents` array updates every screen, which is how a design animates states. - **Theme:** light by default. Put `data-theme="dark"` on a wrapper (or ``) and every token switches. Give the page `bg-canvas text-ink` so the background follows the theme. - **Language:** the components' own words (state labels, Close, Cancel) follow the browser's @@ -40,7 +40,7 @@ use `style={{ height: 360 }}` for one-off sizes. The tokens behind them are CSS variables: `--canvas`, `--panel`, `--sunken`, `--line`, `--line-strong`, `--ink`, `--ink-soft`, `--muted`, `--accent`, `--ok`, `--work`, `--warn`, `--danger`. Use `var(--accent)` in inline styles; never hard-code hex for UI colors. -Bot colors are the exception: each bot has its own from `Botloft.AVATAR_PALETTE` (`#FF7A59` +Agent colors are the exception: each agent has its own from `Botloft.AVATAR_PALETTE` (`#FF7A59` Botloft orange, `#5EC8FF`, `#A48BFF`, `#9BE564`, `#FFC857`, `#FF7EB6`, `#3DD9C1`, `#E8D5B0`); `makeBot` hands them out in that order. @@ -56,55 +56,55 @@ const scout = makeBot({ crew, name: "Scout", state: "busy", role: "Finds sources - `state`: `idle`, `busy` (working), `needs_approval`, `launching`, `rate_limited`, `backoff`, `auth_error`, `offline` (with `paused: true` it reads Paused). -- A bot's turn is a list for `BotRun`: `chat.reply(bot, markdown)`, `chat.tool(bot, { name: - "WebSearch" | "WebFetch" | "Read" | "Write" | "Bash", summary })`, `chat.askSite(bot, url)`, - `chat.askCommand(bot, command, { explanation })`, `chat.suggestBot(chief, { name, role, model, - instructions, reason })`, `chat.askPlan(bot, markdown)`, `chat.turn(bot, seconds)`. Each takes +- An agent's turn is a list for `BotRun`: `chat.reply(agent, markdown)`, `chat.tool(agent, { name: + "WebSearch" | "WebFetch" | "Read" | "Write" | "Bash", summary })`, `chat.askSite(agent, url)`, + `chat.askCommand(agent, command, { explanation })`, `chat.suggestBot(chief, { name, role, model, + instructions, reason })`, `chat.askPlan(agent, markdown)`, `chat.turn(agent, seconds)`. Each takes `{ status: "allowed" | "denied" }` to show it answered. `ApprovalCard` takes one of them's `.body`. - `chat.askRoutine(bot, { name, prompt, schedule, bot: "writer" })` is a bot asking to set up a - routine (for itself, or for another bot by handle). -- Routines, for `CrewRoutines` (pass them as `BotloftProvider` `routines`): `makeRoutine({ bot, + `chat.askRoutine(agent, { name, prompt, schedule, agent: "writer" })` is an agent asking to set up a + routine (for itself, or for another agent by handle). +- Routines, for `CrewRoutines` (pass them as `BotloftProvider` `routines`): `makeRoutine({ agent, name, prompt, schedule: schedule.weekly([5], "09:00") | schedule.weekdays("08:30") | schedule.every(120), lastRun: { status: "done" | "failed" | "queued" | "skipped" } })`. Days are 1 = Monday ... 7 = Sunday. The next run follows the schedule. `status: "queued"` reads "Running now": a new `routines` array with it shows a routine starting. -- `makeMessage({ from: bot | "owner" | "system", to: bot, body, kind: "note" | "task" })` for - `InboundRow`: yours on the right, a bot's or Botloft's on the left. +- `makeMessage({ from: agent | "owner" | "system", to: agent, body, kind: "note" | "task" })` for + `InboundRow`: yours on the right, an agent's or Botloft's on the left. ## Components and their roles - `BotAvatar` is the mascot: `color`, `size`, `mood` (idle, working, waiting, tired, sleeping; it animates), `framed` for Botloft itself on its black square. -- `BotStateBadge` shows a bot's state with icon and word; `ChiefBadge` marks a crew's lead bot. +- `BotStateBadge` shows an agent's state with icon and word; `ChiefBadge` marks a crew's lead agent. - `Button` variants: `primary` (one per view), `secondary`, `ghost` (toolbars), `danger`. - `Dialog`, `Confirm` are modal (fixed, covering the window). `Callout` for notices in a page, `Toaster` + `notifyError(what, error)` for failures away from a form. - Fields: `TextField`, `TextArea`, `SelectField` (labeled), `Select` (custom dropdown), `Choices` (2-6 options side by side), `Switch`, `Tabs`, `Menu`, `Details`, `SidePanel`. -- App screens: `Sidebar` (crews and bots, 288px wide), `CrewBots` (a crew's bot cards), `BotRun` - (one turn of a bot in its chat, inside a `
    `), `InboundRow` (a message in a chat, inside a - `
      `), `ApprovalCard` (a request: a website, a command, a suggested bot, a plan, a routine), - `BrowserPanel` (a bot's browser, live: tabs, address bar, the page and the bot's cursor), - `CrewRoutines` (a crew's routines: each with its bot, schedule in words, next run, last run; +- App screens: `Sidebar` (crews and agents, 288px wide), `CrewBots` (a crew's agent cards), `BotRun` + (one turn of an agent in its chat, inside a `
        `), `InboundRow` (a message in a chat, inside a + `
          `), `ApprovalCard` (a request: a website, a command, a suggested agent, a plan, a routine), + `BrowserPanel` (an agent's browser, live: tabs, address bar, the page and the agent's cursor), + `CrewRoutines` (a crew's routines: each with its agent, schedule in words, next run, last run; it fills the chat column's place, ``). `BotloftProvider`'s `onAnswer` hears Allow and Deny on request cards. - **The app window:** one row, `className="flex bg-canvas text-ink"` with a fixed height (`style={{ height: 820 }}`): the `Sidebar` as its **direct first child**, then the chat column - (`flex min-w-0 flex-1 flex-col`), then, when a bot's browser is open, `` as the **last direct child**. The Sidebar stretches to the row's full height, with the account pinned at the bottom-left; wrapped in any other element it shrinks to its content and looks cut short. Never wrap the Sidebar or the BrowserPanel. Give the window at least 1400px of width when the browser is open, so the chat keeps its room. -- **A browser:** pass `browsers={[{ bot: scout, url, title, tabs: [{ url, title }], action: { kind: +- **A browser:** pass `browsers={[{ agent: scout, url, title, tabs: [{ url, title }], action: { kind: "click", x, y, label } }]}` to `BotloftProvider`. The page is drawn for you (title, address, - text lines); `action` moves the bot's cursor (`open`, `click`, `type`, `scroll`, `back`), and a - new `browsers` array with another `url` or `action` makes the bot browse on. + text lines); `action` moves the agent's cursor (`open`, `click`, `type`, `scroll`, `back`), and a + new `browsers` array with another `url` or `action` makes the agent browse on. Read `components///.prompt.md` for each one's props. ## Voice -Plain words for people who are not programmers. Name bots by their name ("Scout is waiting for +Plain words for people who are not programmers. Name agents by their name ("Scout is waiting for you"), never "the agent" or "Claude". Never say "daemon"; say "Botloft" or "in the background". ## Example @@ -123,12 +123,12 @@ function AppWindow() { const scoutNow = allowed ? { ...scout, state: "busy" } : scout; const card = allowed ? { ...site, body: { ...site.body, status: "allowed" } } : site; return ( - setAllowed(answer.allow)}>
          {/* direct child of the row: it runs the full height */}
            -
          diff --git a/CLAUDE.md b/CLAUDE.md index d5407a8f..8ddc3209 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Botloft -Workspace desktop Windows-first, com código público sob a FSL (vira Apache 2.0 após dois anos), para rodar bots de agentes de código sempre ligados que colaboram entre si: Claude Code, Antigravity e Codex (os dois últimos experimentais). A seção 30 da spec e o ADR 0003 dizem como cada agente entra. Daemon Rust (`botloftd`) + app Tauri v2 + React. +Workspace desktop Windows-first, com código público sob a FSL (vira Apache 2.0 após dois anos), para rodar agentes de IA sempre ligados que colaboram entre si, sobre Claude Code, Antigravity e Codex (os dois últimos experimentais). A seção 30 da spec e o ADR 0003 dizem como cada agente entra. Daemon Rust (`botloftd`) + app Tauri v2 + React. ## Fonte de verdade @@ -65,6 +65,7 @@ Em dev, use `$env:BOTLOFT_HOME = "$PWD\.dev\home"` (caminho absoluto) no daemon - A UI depende só da interface `BotloftApi` (`app/src/lib/api.ts`). Testes de componente usam `FakeBotloft`. - Nenhum texto de interface escrito direto no componente: tudo vai em `app/src/i18n/{en,pt-BR,es}/<área>.ts` e é lido com `useT()` (ou `t()` fora do React). Texto novo entra nos três idiomas no mesmo commit; o TypeScript acusa o que faltar (spec 15.6). Palavras simples, sem "daemon" nem outro jargão. - Nunca edite `app/src/lib/protocol.gen.ts` à mão. +- No texto que o dono lê (app, README, notas de release) cada um é um **agente**; "bot" fica só no nome Botloft e em identificadores (`bot_`, `bots.create`). O que roda por baixo (Claude Code, Antigravity, Codex) é o **motor**. - Commits pequenos, no formato Conventional Commits (`feat(courier): ...`). ## Windows: armadilhas conhecidas diff --git a/README.md b/README.md index e84e84f4..d919825e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

          Botloft

          - A crew of AI coding bots that stays on and works together on your computer. + Your AI agents, always on, working together.

          @@ -29,53 +29,53 @@ - Botloft with the Research crew open: seven bots, each with its own color, state and job + Botloft with the Research crew open: seven agents, each with its own color, state and job -Botloft keeps a crew of bots running on your computer, each on the AI coding agent you choose: +Botloft keeps a crew of agents running on your computer, each on the AI coding engine you choose: [Claude Code](https://code.claude.com), or, as experimental options, -[Antigravity](https://antigravity.google) and [Codex](https://developers.openai.com/codex). Each bot +[Antigravity](https://antigravity.google) and [Codex](https://developers.openai.com/codex). Each agent has its own folder, memory and conversation. They pass work to each other, keep going after you close the window and pick up where they left off after a restart. You talk to each one like in a chat app, and you decide what they may do. -Claude Code is the full experience. Antigravity and Codex came in 0.15.0 as experimental agents, -off until you turn them on (see [Agents](#agents)). +Claude Code is the full experience. Antigravity and Codex came in 0.15.0 as experimental engines, +off until you turn them on (see [Engines](#engines)). ## What you can do - **Start a crew from a goal.** Say what the crew is for. Its Chief plans the work and suggests the - bots it needs; you create each one with a click, or say why not. -- **Pick ready-made bots.** The Bot agency has dozens of bots ready for a job, like a developer, a + agents it needs; you create each one with a click, or say why not. +- **Pick ready-made agents.** The Agent agency has dozens of agents ready for a job, like a developer, a designer, a writer, a researcher or customer support. Add one to a crew with a click and change - anything about it afterwards. The Chief uses the same list when it suggests a bot. -- **Chat with every bot.** Replies stream in as they are written. Send images and files. When a bot + anything about it afterwards. The Chief uses the same list when it suggests an agent. +- **Chat with every agent.** Replies stream in as they are written. Send images and files. When an agent wants to run a command, edit a file or open a website, a card asks you first, unless you told that - bot it may. Search every chat with Ctrl+K. -- **Answer their questions.** A bot that needs your decision asks in its chat and in the Questions + agent it may. Search every chat with Ctrl+K. +- **Answer their questions.** An agent that needs your decision asks in its chat and in the Questions box, and goes on with what it can, even in a routine at night. Your answer reaches it as a message whenever you reply. -- **Let them hand work to each other.** Bots send messages and tasks to each other. Every message is +- **Let them hand work to each other.** Agents send messages and tasks to each other. Every message is saved before it is sent and retried until it arrives. -- **Watch them work.** See every page a bot opens in its browser, live, and take control to sign in +- **Watch them work.** See every page an agent opens in its browser, live, and take control to sign in for it. Sites that refuse to sign in there, like Google, take one click more: sign in in a real - window of the bot's browser, close it, and the bot keeps the login. Watch the screens it designs + window of the agent's browser, close it, and the agent keeps the login. Watch the screens it designs take shape while it writes them. Open the files it made. -- **Let them use your desktop apps (Windows).** A bot can read and use the apps open on your +- **Let them use your desktop apps (Windows).** An agent can read and use the apps open on your computer, like a spreadsheet or an old program with no website, but only those you allow. It asks in the chat, and seeing an app and using it are separate permissions. You watch it live, and - Ctrl+Alt+Esc stops every bot on your desktop. Giving a bot the whole desktop, or letting it work + Ctrl+Alt+Esc stops every agent on your desktop. Giving an agent the whole desktop, or letting it work while you are away, first shows a screen that spells out the risks. Botloft itself, terminals, password managers and password fields are never shared. - **Schedule routines.** Weekday mornings, every two hours or any cron schedule, with missed runs and overlaps handled. -- **Leave them running.** Bots keep working after you close Botloft. They start when you sign in, +- **Leave them running.** Agents keep working after you close Botloft. They start when you sign in, come back soon after a crash and keep the computer awake while they work. Botloft - waits near the clock and tells you when a bot needs you. -- **Choose per bot.** Which model it uses and how much it asks before acting. See how much of your + waits near the clock and tells you when an agent needs you. +- **Choose per agent.** Which model it uses and how much it asks before acting. See how much of your plan's usage is left. - **Connect your own tools.** Add a tool of yours in Settings > Connected tools, like a LinkedIn - reader or your company's system, and turn it on only for the bots that need it. They still ask you + reader or your company's system, and turn it on only for the agents that need it. They still ask you before using it. - **Back up and move.** Save a sealed backup file, or sign in with your e-mail (no password) to keep a light copy in the cloud and restore it on another computer. The server cannot read it. Turn on @@ -83,7 +83,7 @@ off until you turn them on (see [Agents](#agents)). themselves (Windows for now). - **Keep up from your phone.** Choose "Connect a phone" in Settings > Account and phone and scan the code with your phone's camera: Botloft opens there as a web page you can add to the Home Screen. - Approve requests, answer questions and chat with your bots from anywhere, with a notice when one + Approve requests, answer questions and chat with your agents from anywhere, with a notice when one needs you. What passes between the phone and your computer is sealed end to end: the server carries it but cannot read it. Anyone holding the phone unlocked can do the same, so you can lock the page with a PIN. It uses the same account; files stay on your computer. Checked on Android, @@ -95,7 +95,7 @@ off until you turn them on (see [Agents](#agents)). Designer writing a bakery landing page that takes shape in the screens area, with its cursor on the part being written
          Screens. Designer's pages take shape while it writes them. - The Chief suggesting a new bot, Designer, with its name, model, role and instructions ready to edit
          A Chief for each crew. It suggests the bots the work needs. + The Chief suggesting a new agent, Designer, with its name, model, role and instructions ready to edit
          A Chief for each crew. It suggests the agents the work needs. The crew's routines: a morning summary on weekdays, a link check every two hours, weekly numbers and a nightly archive
          Routines. Work that runs on a schedule, with the last result at a glance. @@ -103,7 +103,7 @@ off until you turn them on (see [Agents](#agents)). Botloft speaks English, Portuguese (Brazil) and Spanish, follows your light or dark theme and updates itself. -## Agents +## Engines Antigravity and Codex are experimental and off by default. Claude Code and Antigravity compare like this: @@ -111,15 +111,15 @@ like this: | | Claude Code | Antigravity (experimental) | |---|---|---| | Chat, crew messages, tasks, routines, the live browser | yes | chat, crew messages and tasks; the rest is not checked yet | -| Asks you before acting | yes: each tool, with "always allow" | no: it cannot ask. It works with files in its own folders; commands you list per bot (or "full access") | +| Asks you before acting | yes: each tool, with "always allow" | no: it cannot ask. It works with files in its own folders; commands you list per agent (or "full access") | | Images | yes | yes, by file | | Model | your plan's default, or one you pick | its own list; the effort is part of the name | | Usage meter and "Compact" | yes | an estimate of the conversation size; no compaction | | Isolation between crews | yes | folders are fenced for its file tools, not for commands you allow | Claude Code is the full experience. Antigravity and Codex are off until you list them in Botloft's -`config.toml` (`experimental_agents = ["agy"]`, `["codex"]` or both); then a new bot can be made on -them, and Settings has "Agent for new bots". A Codex bot asks you before it changes a file or runs a +`config.toml` (`experimental_agents = ["agy"]`, `["codex"]` or both); then a new agent can be made on +them, and Settings has "New agents run on". A Codex agent asks you before it changes a file or runs a command, like Claude Code, and shows the exact size of its conversation; it can still read any file. It has its own model list and effort picker, and understands images. See [spec section 30](docs/spec.md) for what was checked. @@ -127,8 +127,8 @@ checked. ## Get started You need [Claude Code](https://code.claude.com/docs/en/setup), installed and signed in once with -your own account. Bots run on your own plan. (Antigravity and Codex, the experimental agents, work -the same way with their own accounts; see [Agents](#agents).) +your own account. Agents run on your own plan. (Antigravity and Codex, the experimental engines, work +the same way with their own accounts; see [Engines](#engines).) 1. Download Botloft for your computer from the [latest release](https://github.com/httpsphl/botloft/releases/latest): @@ -152,8 +152,8 @@ On Windows, uninstalling (Settings > Apps) removes the app and stops Botloft fro background. On Linux and macOS, stop it first with `~/.local/share/Botloft/bin/botloftd service uninstall` (Linux) or `~/"Library/Application Support/Botloft/bin/botloftd" service uninstall` (macOS), then remove the -app. Your bots and their files stay either way (see -[Where are my bots and their files?](#faq)). +app. Your agents and their files stay either way (see +[Where are my agents and their files?](#faq)). ## How it works @@ -161,41 +161,41 @@ app. Your bots and their files stay either way (see flowchart LR app["Botloft app
          Tauri + React"] -- "JSON-RPC over WebSocket
          127.0.0.1 only" --> daemon["botloftd
          Rust daemon"] daemon --> db[("SQLite")] - daemon -- "stdin and stdout
          stream-json" --> bots["One agent process
          per bot"] - bots -- "Botloft's MCP tools
          messages, tasks, requests" --> daemon + daemon -- "stdin and stdout
          stream-json" --> agents["One process
          per agent"] + agents -- "Botloft's MCP tools
          messages, tasks, requests" --> daemon ``` -- **The daemon is the source of truth.** Closing the app never stops a bot. `botloftd` runs as a +- **The daemon is the source of truth.** Closing the app never stops an agent. `botloftd` runs as a scheduled task of your Windows user, a systemd user service on Linux or a launch agent on macOS: it starts when you sign in and comes back soon if it dies. -- **Your agent is the runtime.** Each bot is a real session of the agent you chose (`claude -p` for +- **Your engine is the runtime.** Each agent is a real session of the engine you chose (`claude -p` for Claude Code; `agy -p` for Antigravity and `codex app-server` for Codex) with its own workspace, memory and conversation. There is no custom agent SDK and no API key to manage. -- **One conversation per bot.** Your messages, messages from other bots and notices from the daemon - reach the bot in order. What it does comes back as a chat: replies, tool use and requests you +- **One conversation per agent.** Your messages, messages from other agents and notices from the daemon + reach the agent in order. What it does comes back as a chat: replies, tool use and requests you allow or deny. - **Durable before delivered.** Every message is written to SQLite before it is sent and retried until it is delivered or marked as failed, which you can retry. -The full design, from the protocol to the states of a bot, lives in [`docs/spec.md`](docs/spec.md) +The full design, from the protocol to the states of an agent, lives in [`docs/spec.md`](docs/spec.md) (in Portuguese). ## Privacy and security - **Local first.** The daemon listens on `127.0.0.1`, and there is no telemetry. An account is - optional. Without one, Botloft uses the network for nothing beyond updates and your bots' work: - what they send to the agent's provider, which goes through the agent as when you use it yourself, + optional. Without one, Botloft uses the network for nothing beyond updates and your agents' work: + what they send to the engine's provider, which goes through the engine as when you use it yourself, and the websites and connected tools they use. With one, the server holds only backups sealed with a password that never leaves your computer, and passes along what a connected phone and your computer say to each other, sealed end to end (see the FAQ). -- **You decide what bots may do.** Per bot: ask before anything, accept edits, plan only, or decide on - its own what needs your OK. Unless it decides on its own, a bot also asks before its first visit +- **You decide what agents may do.** Per agent: ask before anything, accept edits, plan only, or decide on + its own what needs your OK. Unless it decides on its own, an agent also asks before its first visit to each website. - **Tokens stay safe.** The app authenticates with an owner token readable only by your user account. - Each bot gets its own token per start, and the daemon keeps only its SHA-256 hash. -- **Botloft is not a sandbox.** Every bot runs as your user account, so isolation between bots is - cooperative: a bot can read other bots' folders if it decides to. Give bots only the permissions - you would give that agent directly. + Each agent gets its own token per start, and the daemon keeps only its SHA-256 hash. +- **Botloft is not a sandbox.** Every agent runs as your user account, so isolation between agents is + cooperative: an agent can read other agents' folders if it decides to. Give agents only the permissions + you would give that engine directly. What Botloft sends over the network, what it changes on your computer and how to remove it are in the [code signing policy](docs/code-signing-policy.md). @@ -206,22 +206,22 @@ the [code signing policy](docs/code-signing-policy.md).

          Does Botloft cost anything? No. Botloft is free to use, at home or at work, and its source code is public (see -[License](#license)). The bots run on the agent you chose, with +[License](#license)). The agents run on the engine you chose, with your own account, so they use your plan's limits; with Claude Code, Botloft shows how much is left.
          Do I need to keep the window open? -No. The bots run in the background, even after you close Botloft, and start when you sign in if you +No. The agents run in the background, even after you close Botloft, and start when you sign in if you want. Settings has a switch to stop them when you close the app instead.
          -Where are my bots and their files? +Where are my agents and their files? -Each bot has a folder under `Botloft\\` in your home folder (`%USERPROFILE%` on Windows, +Each agent has a folder under `Botloft\\` in your home folder (`%USERPROFILE%` on Windows, `~` on Linux and macOS), and each crew a shared work folder that you can point at a project of yours. Botloft's own data (SQLite, logs, secrets) is in `%LOCALAPPDATA%\Botloft` on Windows, `~/.local/share/Botloft` on Linux and `~/Library/Application Support/Botloft` on macOS. @@ -231,7 +231,7 @@ yours. Botloft's own data (SQLite, logs, secrets) is in `%LOCALAPPDATA%\Botloft` What does the optional account keep, and who can read it? You sign in with a link sent to your e-mail; there is no account password. The cloud copy is the -light one: your crews' structure, the bots' memory and the routines, not the chats or files. Botloft +light one: your crews' structure, the agents' memory and the routines, not the chats or files. Botloft seals it on your computer with the backup password before sending, so the server stores bytes it cannot open. It also knows your e-mail, the computers you signed in on, the phones you connected (and, if you turn on notices, where to send them), and the size and date of each copy. Lose the @@ -273,19 +273,19 @@ as installed on your computer and is not affiliated with or endorsed by Anthropi ## Status -Botloft is young (version 0.x) and moves fast. The latest release, 0.15.0 (2026-10-10), has bots -that keep running in the background, the chat, crews with a Chief, ready-made bots in the Bot -agency, messages and tasks between bots, routines, the live browser, screens, "always allow" for -requests, questions from bots, chat search, desktop use on Windows, connected tools, backups with an +Botloft is young (version 0.x) and moves fast. The latest release, 0.15.0 (2026-10-10), has agents +that keep running in the background, the chat, crews with a Chief, ready-made agents in the Agent +agency, messages and tasks between agents, routines, the live browser, screens, "always allow" for +requests, questions from agents, chat search, desktop use on Windows, connected tools, backups with an optional cloud copy, the phone page, the installer and updates, and Antigravity and Codex as -experimental agents. Still open: a code-signed installer. +experimental engines. Still open: a code-signed installer.
          Development Requirements: Rust stable (the MSVC toolchain on Windows), Node.js 24 and pnpm. On Linux, the app also needs the desktop libraries Tauri uses (on Ubuntu: `libwebkit2gtk-4.1-dev`, -`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real bots also needs an agent +`libayatana-appindicator3-dev`, `librsvg2-dev`, `libxdo-dev`). Running real agents also needs an engine installed and signed in: Claude Code (the native `claude` executable), Antigravity (`agy`) or Codex. ```powershell @@ -314,7 +314,7 @@ macOS): botloftd service install # copy botloftd into the data folder, start it now and at every logon botloftd service status botloftd service restart -botloftd service uninstall # stop it and remove the task; bots and data stay +botloftd service uninstall # stop it and remove the task; agents and data stay ``` Each data folder gets its own task, so a dev daemon installed with `BOTLOFT_HOME` (or `--home`) diff --git a/SECURITY.md b/SECURITY.md index 1e00f28a..f4ba4806 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -16,16 +16,16 @@ The app updates itself, so staying on it is one click. ## Scope -Botloft runs Claude Code bots on your computer, with your account. Some things are by design and not +Botloft runs AI agents on your computer, with your account. Some things are by design and not vulnerabilities: -- Bots run as your user, without a sandbox. Keeping them apart is cooperative: a bot that may run +- Agents run as your user, without a sandbox. Keeping them apart is cooperative: an agent that may run any command (or one set to skip permission prompts) can read what your user can read, including - other bots' folders and Botloft's own data. The app warns about this before you turn that on. + other agents' folders and Botloft's own data. The app warns about this before you turn that on. - The background service listens on `127.0.0.1` only and asks every client for a token, which only your user can read. Other programs running as your user are trusted as much as you are. -A way around what a bot was allowed to do, a page or a message that gets a bot past a permission +A way around what an agent was allowed to do, a page or a message that gets an agent past a permission prompt, or a program reaching the service without the token is in scope and very welcome. Antivirus detections of the releases are not security reports: see the diff --git a/crates/botloftd/Cargo.toml b/crates/botloftd/Cargo.toml index 822c7d1c..744c851e 100644 --- a/crates/botloftd/Cargo.toml +++ b/crates/botloftd/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "botloftd" -description = "Botloft daemon: runs and supervises always-on Claude Code bots" +description = "Botloft daemon: runs and supervises always-on AI agents" version.workspace = true edition.workspace = true license.workspace = true