Laki Agent Docs

Command reference

Every Laki Agent capability, grouped by family, with its interface and safety class.

The complete surface of Laki Agent, grouped by family. Safety classes: *read-only* and *free* run without approval; *gated* requires human approval; *auth* requires an approved Laki Bits session; *na* marks internal events.

Laki Bits Auth (app gate)

Loopback Firebase sign-in that gates the entire app. Every agent run and terminal open re-checks for an approved Laki Bits session as defense-in-depth; the stored token is re-verified against laki-api on each status check to catch expiry/revocation. Implemented as ipcMain.handle channels in ipc.js, exposed on window.laki.auth in preload.js.

CommandInterfaceSafetyWhat it does
auth:statusipcread-onlyReports whether the user is signed in with an approved Laki Bits account. Re-verifies the stored token against laki-api on every call, so it catches expiry, revocation, or a not-yet-approved account and reports the reason.
auth:loginipcauthStarts the loopback Firebase sign-in flow (opens a browser). Enforces account approval and throws otherwise. On success, broadcasts auth:changed to the UI.
auth:logoutipcauthClears the stored Laki Bits session and broadcasts the signed-out state to the UI.
auth:changedinternaleventPush event the main process sends to the renderer whenever sign-in state changes, so the UI can gate/ungate the app live.

Embedded Browser Control

Drive-the-browser wrapper over the embedded Chromium WebContentsView tabs (browser.js + tabs.js), plus multi-tab lifecycle (new/close/activate/list), layout/region control, and a 'sign out of Salesforce' data wipe. Navigation, back/forward/reload/stop, and view geometry. All tabs share one persistent Salesforce partition (a new tab into a signed-in org is already logged in); exactly one tab is visible at a time. Distinct from the agent's read-only 'browser sight' tools (CDP, see agent tools). URL input is normalized (bare domains, full URLs, or Google search fallback).

CommandInterfaceSafetyWhat it does
browser:navigateipcfreeLoads a URL in the embedded Chromium view. Accepts a bare domain, a full URL, or plain search text (which becomes a Google search).
browser:backipcfreeGoes back one entry in the embedded browser's navigation history, if possible.
browser:forwardipcfreeGoes forward one entry in the embedded browser's navigation history, if possible.
browser:reloadipcfreeReloads the current page in the embedded browser.
browser:stopipcfreeStops the in-flight page load in the embedded browser.
browser:setRegionipcfreeTells the main process the pixel rectangle where the native browser view should sit, so the surrounding chrome (address bar + sidebar) frames it correctly.
browser:setHiddenipcfreeHides the native browser view while an HTML modal is open (else the modal renders behind it), and shows it again on close.
browser:clearDataipcgatedWipes the persistent 'salesforce' partition's cookies and storage, effectively signing the user out of all Salesforce sessions in the embedded browser.
tabs:newipcfreeCreates a new embedded browser tab and makes it active. Optional url (defaults to the Salesforce login home). All tabs ride the shared 'persist:salesforce' partition, so a new tab into an already-signed-in org is already logged in.
tabs:closeipcfreeCloses a tab by id, detaching its CDP first and activating a neighbor if it was active. Refuses to close the last remaining tab.
tabs:activateipcfreeMakes the given tab the active (visible) one, lazily loading its page on first activation-while-signed-in and syncing the address bar to it.
tabs:listipcread-onlyReturns the current tab strip ({tabs:[{tabId,title,url,favicon,active,loading}], activeTabId}) for initial paint before the renderer subscribes to tabs:state.
tabs:stateinternaleventPush event carrying the full tab list + activeTabId, sent on any create/close/activate/title/favicon/loading change so the strip stays in sync.
browser:stateinternaleventPush event carrying the live {tabId,url,title,canGoBack,canGoForward,loading} so the address bar stays in sync with the embedded browser. Tagged with tabId — the renderer applies it only when it matches the active tab.
browser:downloadinternaleventPush event reporting a download's name, state (completed/cancelled/…), and path.

Browser Sight (agent tools, CDP)

The agent's read-only 'line of sight' into the embedded page(s), delivered as agent-loop tool names (agent.js) backed by CDP instrumentation (cdp.js). These run FREELY with no approval, like the Salesforce read tools. CDP attaches LAZILY on first read (never during SSO/MFA login, to avoid tripping bot-detection). Multi-tab: reads target the agent's tab (the active tab by default; open_tab/switch_tab re-pin it); read_page/list_network accept an optional tabId. The reads are also exposed to the renderer as a manual debug panel via inspect:* channels.

CommandInterfaceSafetyWhat it does
read_pageagent-toolread-onlyReturns a model-sized snapshot of the rendered DOM the user is looking at: url, title, visible body text (clipped), headings, links, and buttons. Reflects post-Lightning-hydration render, not raw HTML.
list_networkagent-toolread-onlyReturns the recent network requests the page made (method, url, type, status, mimeType) from a rolling 250-entry per-tab buffer. Lets the agent reason about a SaaS app the way a developer with DevTools open would.
get_urlagent-toolread-onlyReturns the current page URL of the agent's target tab.
navigateagent-toolfreeLoads a URL in the agent's target tab from within the agent loop. Runs freely (not a write proposal).
list_tabsagent-toolread-onlyReturns the open browser tabs [{tabId, title, url, active}] so the agent can see and target multiple orgs/areas at once.
open_tabagent-toolfreeOpens a NEW browser tab at a url and makes it the agent's target (and the visible tab), so the agent can work in a second org/area without disturbing the user's current tab. Runs freely (navigation, not a write).
switch_tabagent-toolfreeMakes an existing tab the agent's target (and the visible one) for subsequent reads. Runs freely.
inspect:readDomipcread-onlyManual, renderer-facing version of read_page for a debug/inspection panel: the same CDP DOM snapshot, invoked directly by the UI rather than the agent.
inspect:listNetworkipcread-onlyManual, renderer-facing version of list_network for a debug/inspection panel: the recent CDP network log, invoked directly by the UI.

Model Providers & BYO Keys

Provider management for the agent's LLM: BYO Anthropic/OpenAI keys (Keychain-backed via secrets.js) plus the NEW keyless 'claude-cli' provider that piggybacks on the user's already-authenticated Claude Code CLI (subscription login, no API key stored). The provider config channels (settings:*) are exposed on window.laki.settings; the active provider drives agent.js's complete(). claude-cli scrubs billing/auth env vars and disables all built-in CLI tools so the child is a pure text turn.

CommandInterfaceSafetyWhat it does
settings:getProvidersipcread-onlyReturns the list of configured model providers and which one is active (never returns raw API keys, which live in the OS keychain).
settings:saveProvideripcauthSaves or adds a model provider config (provider type, model, base URL, optional API key). API keys are stored in the OS keychain, not in plain config.
settings:setActiveipcfreeMarks one saved provider as the active model the agent will use for its next run.
settings:deleteProvideripcgatedRemoves a saved provider config (and its stored key) from the app.
settings:checkClaudeCliipcread-onlyAvailability probe for the keyless 'Claude CLI' provider: resolves the claude binary through a login shell and validates --version. Reports whether Claude Code is installed and signed in, with its version or an error. No key required — it reuses the user's Claude Code subscription login.
settings:checkConnectionsipcread-onlyThe 3-way 'is everything actually connected?' check: is the active MODEL reachable (Claude CLI login or a BYO key), is the SALESFORCE CLI authenticated to any org, and does the org the browser is CURRENTLY showing match one of the sf-authenticated orgs. Uses the browser's real current URL, never a renderer-supplied value.
claude-cli-completeinternaleventThe keyless provider's completion backend: shells out to `claude -p --output-format json` over stdin, honoring the user's Claude Code subscription. Scrubs ANTHROPIC_API_KEY/AUTH_TOKEN/BASE_URL/MODEL and all CLAUDE_CODE_* env vars to prevent billing/config diversion, runs with --tools "" (all built-in tools disabled) in an empty scratch dir, and checks both the exit code AND the JSON is_error flag.

Salesforce READ Tools (agent, free)

Read-only Salesforce CLI wrappers (sfcli.js READ_TOOLS) the agent may call FREELY inside the ReAct loop — no approval. Each shells out to the user's own sf ... --json, inheriting the user's org auth via --target-org (tokens are never handled). The org alias is INJECTED by the loop from the model's args (alias/org/targetOrg), never trusted blindly. These are advertised to the model via readCatalog() in the system prompt.

CommandInterfaceSafetyWhat it does
list_orgsagent-toolread-onlyLists the orgs the local sf CLI is authenticated to (alias, username, edition, isDevHub, instanceUrl), de-duped across sf's org buckets. Needs no org alias.
org_infoagent-toolread-onlyReturns identity and instance info for the target org (username, id/orgId, instanceUrl, apiVersion, status, edition). Deliberately omits the access token.
list_sobjectsagent-toolread-onlyLists SObjects rolled up by namespace — managed_namespaces reveal installed managed packages (e.g. SBQQ = Salesforce CPQ), plus a total and a small name sample. The best way to check whether a package is installed.
describe_sobjectagent-toolread-onlyDescribes one SObject's fields (name, type, label, required, refersTo) and child relationships, trimmed to fit model context. Used to discover real API names before querying or proposing a write.
soql_queryagent-toolread-onlyRuns a read-only SOQL query (the raw LakiQL capability) and returns totalSize plus cleaned records (Salesforce 'attributes' noise stripped, capped at 200 rows).

Salesforce WRITE Tools (human-approved gate)

The write path (sfcli.js WRITE_TOOLS + agent.js propose flow). The model can NEVER write directly — it may only PROPOSE a write via the 'propose' JSON form. sfcli.planWrite() builds a WritePlan (summary, targetOrg, targetEdition, exact command, materialized files, warnings); the agent surfaces it via the approval:request round-trip; only after a human clicks approve does applyPlan() materialize files into a throwaway sfdx project and deploy. On rejection the model is told not to re-propose. targetEdition is read so a human can refuse a live/prod org.

CommandInterfaceSafetyWhat it does
create_custom_fieldwrite-toolgatedProposes creating ONE custom field on an object by deploying a CustomField. The plan shows the exact field (object.API name, type, label), the target org and edition, and warnings (e.g. new fields have no field-level security until granted via a permission set). Nothing runs until a human approves.
approval:requestinternaleventPush event carrying the WritePlan (or gated-command plan) to the renderer so a human can review the exact change and approve or reject it. This is the human-in-the-loop gate.
approval:respondipcgatedThe human's response to a pending write/command approval. Resolves the pending gate promise with true (approve) or false (reject); on false the corresponding write/command never runs.

run_command (agent CLI, free vs gated)

A general CLI tool for the agent (sfcli.js run_command helpers + commands.js classifier). The model passes argv (preferred) or a command string; commands.toArgv() normalizes and rejects shell metacharacters. commands.classify() is the deny-by-default security boundary: only a positively allow-listed {bin, subcommand} with no denied flag runs 'free' (immediately); everything else is 'gated' and routed through the SAME human approval gate as writes. Uses spawn(shell:false) + argv array (no shell-metachar injection) + a per-bin denied-flag scan (argument-injection defense) + wall-clock timeout + per-stream output cap.

CommandInterfaceSafetyWhat it does
run_command:freeagent-toolfreeRuns a single read-only CLI command immediately, with no approval. Only positively allow-listed programs+subcommands qualify: sf reads (org list/display, sobject list/describe, data query, apex log read, limits, deploy report), sfdx legacy reads, git reads (status/log/diff/show/branch/remote/rev-parse/describe/ls-files/shortlog/tag/config --get/--list), npm reads (ls/view/outdated/why/ping/config get), and pwd/ls/which/echo/whoami/date/node --version/python(3) --version. Top-level --version/--help are always free.
run_command:gatedagent-toolgatedAny command not on the read-only allowlist (deploys, DML, data import, git push/commit, npm install, file writes, unknown programs, a denied flag like git -c/--exec-path, or any chaining/redirect) is turned into a WritePlan and shown to a human for approval FIRST — the exact argv, working directory, target org, reason, and warnings are displayed. Nothing runs until approved; a rejected command must not be re-proposed.

Terminal Tab (human's own shell)

A persistent user shell the HUMAN drives directly (terminal.js), streamed to the renderer via term:data. This is the human's channel (same trust as opening Terminal.app) — distinct from the model's gated run_command. Pure-Node child_process pipe (no PTY, no native dep), so full-screen curses apps won't render but sf/git/npm/line output work. open()/input() are gated behind an approved Laki Bits session (same check as agent:send). Runs the login shell with TERM=dumb, PAGER/GIT_PAGER=cat.

CommandInterfaceSafetyWhat it does
term:openipcauthOpens (or reuses) a persistent login-shell session and starts streaming its merged stdout+stderr to the UI. Returns the working directory and shell. Requires an approved Laki Bits session.
term:inputipcauthWrites the user's keystrokes/command text to the shell's stdin. Requires an approved session.
term:interruptipcauthSends SIGINT to the running shell child — best-effort Ctrl-C without a PTY (signals the child, not a full process group).
term:closeipcauthKills the shell session (SIGKILL) and tears down its output stream.
term:datainternaleventPush event streaming merged stdout+stderr chunks from the shell to the UI as they arrive, plus exit/error markers.

Agent Run Lifecycle

The top-level agent run channels (ipc.js) that drive the ReAct loop in agent.js. agent:send is the entry point (re-checks for an approved session as defense-in-depth, enforces a single concurrent run, and best-effort mirrors the run to laki-api for read-only web replay); agent:cancel aborts via AbortController; agent:event streams the live trace. The loop itself dispatches the browser-sight, Salesforce-read, run_command, and write-proposal tools cataloged in the other families.

CommandInterfaceSafetyWhat it does
agent:sendipcauthRuns the agent's ReAct loop on a natural-language message: model -> tool calls (browser sight, SF reads, run_command) -> write proposals gated by human approval -> a final markdown answer, streamed live. Refuses without an approved Laki Bits session and refuses if a run is already in progress.
agent:cancelipcfreeAborts the in-flight agent run via its AbortController.
agent:eventinternaleventPush event streaming the live run trace to the UI: assistant thoughts, tool calls with observations, approval requests, the final answer, and errors.