diff --git a/app/src/core/AgentCore.cpp b/app/src/core/AgentCore.cpp index 89d8216..16f25dc 100644 --- a/app/src/core/AgentCore.cpp +++ b/app/src/core/AgentCore.cpp @@ -45,6 +45,24 @@ class Turn : public QThread { QByteArray m_prompt; }; +/* The engine asks for a tool only when its own confirm_shell / confirm_write / + * confirm_net flag says so (`needs_confirm()`, src/agent_loop.c) -- the + * approval mode on top of that only *relaxes* them (AUTO writes a file + * without asking, YOLO everything). The CLI fills those three in + * (src/main.c: parse_confirm), the TUI and the WebUI do too; the window never + * did: AgentCore::open() zeroed the whole config and cst_agent_create() keeps + * what it is given, so every tool ran without asking while the sidebar said + * "ask". One mapping, used by both open() and setApproval(). */ +namespace { +void applyApproval(cst_agent_config_t *cfg, int approval) { + const int ask = (approval == CST_APPROVAL_ASK); + cfg->approval = approval; + cfg->confirm_shell = ask; + cfg->confirm_write = ask; + cfg->confirm_net = ask; +} +} // namespace + /* --- lifetime ------------------------------------------------------------ */ AgentCore::AgentCore(QObject *parent) : QObject(parent) { @@ -141,6 +159,14 @@ bool AgentCore::open(const QString &workspace, const QString &provider, /* The GUI answers itself: never let the engine decide behind its back. */ m_cfg.auto_approve = 0; m_cfg.headless = 0; + /* What the person chose last -- the CLI and the TUI remember it the same + * way and the TUI writes the same file -- else "ask", which is what the + * window's default label promises and the only mode where its consent + * dialog is ever reached. */ + int approval = CST_APPROVAL_ASK; + if (have_settings && st.approval[0]) + cst_approval_parse(st.approval, &approval); + applyApproval(&m_cfg, approval); if (!m_cfg.mode[0]) snprintf(m_cfg.mode, sizeof(m_cfg.mode), "%s", CST_DEFAULT_MODE); m_agent = cst_agent_create(&m_cfg); @@ -395,9 +421,12 @@ void AgentCore::setMode(const QString &mode) { } void AgentCore::setApproval(int approval) { - m_cfg.approval = approval; + applyApproval(&m_cfg, approval); if (m_agent) - cst_agent_config_mut(m_agent)->approval = approval; + applyApproval(cst_agent_config_mut(m_agent), approval); + /* Remembered like the TUI's /approval: the next start of the window (or of + * the CLI) comes up the way it was left. */ + cst_settings_remember_approval(approval); emit approvalChanged(); } diff --git a/app/src/ui/UiTest.cpp b/app/src/ui/UiTest.cpp index 5349969..118928d 100644 --- a/app/src/ui/UiTest.cpp +++ b/app/src/ui/UiTest.cpp @@ -416,6 +416,25 @@ int runUiTest(AgentCore &core, const QString &size) { QStringLiteral("and the line is where the card is")); } + /* The approval switch is not decoration: the engine asks for a tool only + * when its own confirm_* flags say so (needs_confirm() in agent_loop.c), + * and every front end that has a consent dialog has to set them -- the + * window used to leave all three at zero, which is why "ask" never asked. */ + { + const int was = core.approval(); + core.setApproval(CST_APPROVAL_ASK); + const cst_agent_config_t *live = cst_agent_config(core.agent()); + check(live && live->approval == CST_APPROVAL_ASK && live->confirm_shell && + live->confirm_write && live->confirm_net, + QStringLiteral("'ask' reaches the engine as the three confirm flags")); + core.setApproval(CST_APPROVAL_YOLO); + live = cst_agent_config(core.agent()); + check(live && live->approval == CST_APPROVAL_YOLO && !live->confirm_shell && + !live->confirm_write && !live->confirm_net, + QStringLiteral("and 'yolo' takes them all away")); + core.setApproval(was); /* leave the machine as it was found */ + } + /* --- md_to_pdf: the front end's own document writer ------------------- */ { const cst_tool_def_t *def = cst_tool_find("md_to_pdf");