Bamboo-agent 发布本地 AI 智能体运行时,支持终端、HTTP 与 Rust SDK 接入
bigduu/Bamboo-agent
Bamboo-agent 是 Zenith 核心的本地智能体运行时,给它一个工作区和已配置的模型,就能读取文件、调用工具、保留会话,并把同一运行时暴露给浏览器、桌面外壳或 Rust 应用。
Brand illustration, not a software screenshot. Bamboo symbolizes resilience.
Run an AI agent on your project, from your terminal or your own app.
Bamboo is the local agent harness at the core of Zenith. Give it a workspace and a configured model: it can read files, use tools, keep sessions, and expose the same runtime to a browser, desktop shell, or Rust application.
What you can use it for
| Your task | How Bamboo helps |
|---|---|
| Understand or work on a repository | Run a prompt against a workspace, inspect tool activity, and continue the session later. File and shell access follows the runtime's permission policy. |
| Build an assistant into your app | Use HTTP plus live WebSocket/SSE events, or embed the Rust SDK instead of building another agent loop. |
| Keep useful project context | Session notes and Jiandu-backed durable memory can carry selected facts across turns and sessions. Compression manages context budgets; it cannot guarantee perfect recall. |
| Connect your existing tools | Add MCP servers, skills, service plugins, and scheduled prompts as your workflow needs them. |
The runtime and session storage run locally. Configured model providers, MCP servers, web tools, and plugins may send data outside the machine. Local hosting does not mean an offline model or zero external API cost.
Source checkout or published package?
This README describes the development source checkout, with the original product audit at 025641317c5703226052a4b94a52d1844615c352 and this documentation refresh based on dev revision 10ca51ccdf1f253ff6ec1e78f21b4c74835e1582. It is not a claim that every command or capability is in the latest crates.io release. Check the installed binary's bamboo --help and the release/source audit when comparing versions. Source manifests deliberately use 0.0.0; publishing stamps the release version separately. The recording below retains its separate, older source provenance.
See the browser interface prepare project context
Static image · Recording details
Real source-checkout recording with a disposable workspace and a real Bamboo backend. It shows project creation and selection, with no provider call or claimed agent task completion. It is not a recording of the published desktop package.
Try it
Install
For a published package:
cargo install bamboo-agent --locked bamboo --help
For the source described here, use Rust 1.95+ and run from this checkout:
node scripts/frontend-package.cjs stage
cargo install --path . --lockedThe staging command verifies the committed frontend package. Normal builds require the locked Lotus Next artifact; they fail if it is missing or invalid. They do not silently download a moving latest. The exact identity is in frontend-package-lock.json.
For an intentionally API-only source build:
BAMBOO_FRONTEND_BUILD_MODE=api-only cargo build --bin bamboo
That POSIX shell assignment needs PowerShell's $env:BAMBOO_FRONTEND_BUILD_MODE = "api-only" on Windows. API-only builds have no compiled-in browser UI. See the deployment guide and frontend staging script for explicit external or local frontend paths.
Configure and open the local interface
bamboo init bamboo serve
init interactively configures your provider and stores its key encrypted at rest under the Bamboo data directory (normally ~/.bamboo/). config.json holds configuration metadata rather than the encrypted provider key. Use a model available to your provider/account. With the frontend included, open http://127.0.0.1:9562. In another terminal:
bamboo health bamboo doctor
The health endpoint is GET /api/v1/health; bamboo health requires a reachable server. doctor checks configuration and provider credentials, failing on those errors; its server-reachability probe is informational, so an absent server alone does not make doctor fail. serve --port, --bind, --data-dir, --static-dir, and --workers override configuration; run bamboo serve --help for the full list.
Work from the terminal
bamboo -p "Summarize the README in this workspace." --workspace /path/to/project bamboo sessions bamboo history <session-id> bamboo -p "What should I read next?" -s <session-id>
Headless runs use the full agent runtime and the configured provider. If an interactive bamboo -p run pauses for a tool permission or question, answer the prompt in that same terminal. Browser responses and bamboo respond <session-id> --pending / bamboo respond <session-id> "<answer>" target runs owned by a separately running bamboo serve; they do not unblock the in-process headless run. Do not disable permission checks just to make an example finish.
No key yet? bamboo -p "ping" --echo is a transport smoke test only: it uses an echo executor, not an LLM, and does not demonstrate model reasoning or successful tool work.
Call it from your application
With a configured provider and running server, the legacy HTTP/SSE sequence is chat → subscribe → execute. Native chat saves session configuration and admits a typed Inbox message with its own RespectSpecificWait intent. Canonical history and MessageAppended appear when an existing same-session consumer checkpoints and ACKs it; execute starts the agent loop. The example requires curl and jq; replace the model with one your account supports. In terminal A, create the session and open its live event stream:
SID=$(curl -fsS http://127.0.0.1:9562/api/v1/chat \ -H 'Content-Type: application/json' \ -d '{"message":"Say hello.","model":"YOUR_MODEL_ID"}' | jq -r .session_id) printf 'Session: %s\n' "$SID" curl -iNfsS "http://127.0.0.1:9562/api/v1/events/$SID"
Keep terminal A open. Once its HTTP 200 response headers appear, copy the printed session ID into terminal B and start the run:
SID="<session-id printed in terminal A>" curl -fsS -X POST "http://127.0.0.1:9562/api/v1/execute/$SID" \ -H 'Content-Type: application/json' -d '{}'
Native Chat returns the actual message_id and ingress_seq receipt fields.
Its serialized envelope is limited to 256 KiB and must contain semantic content;
attachments are kept whole, including duplicate images and more than 16 images.
There is no truncation to fit the limit. Chat without a consumer leaves the input
in the Inbox and does not append history, emit MessageAppended, or start a title.
Automatic title work moves to checked Native admission followed by successful
HTTP Ready startup. A running owner or another legitimate same-session activation
may consume the message before the client's execute request. The specific intent
does not release staged child/Bash outcomes; their coordinator retains that order.
Referenced/ticket and Root ingress keep their existing scheduling rules.
Watch terminal A for live events; subscribing after execution can miss response tokens. The browser uses the shared /v2/stream WebSocket; legacy SSE routes remain available.
Use it as a Rust SDK (in-process)
For SDK embedding, bamboo_sdk::agent::Agent provides run, run_stream, and
execute over the same engine. Defaults require a configured provider; they do not
supply credentials. A complete built-in defaults assembly also supports
run_with_skills(session, SdkSkillInput) and its cancellation variant. These async
entries validate current selections and the portable submission hook before appending
the prepared User. They require a new empty Session or the exact durable Session; save
and reload staged edits before submitting. The existing String entries keep their
synchronous append and error order. Explicit tool policies, custom executors and
from_runtime do not supply this finite Skills host and reject typed input before
append. Low-level execute retains its custom-request contract. See first-run CLI /
HTTP / SDK examples, API
reference, and the published crate's
rustdoc.
Your data and operating boundaries
- Bamboo configuration and sessions default to
~/.bamboo(BAMBOO_DATA_DIRor--data-dircan override it). Jiandu owns canonical memory separately under~/.jiandu.BAMBOO_JIANDU_DATA_DIRmust be a non-empty absolute path when used for isolation;--data-diralone does not relocate memory. - Bamboo selects prompt context and budgets; Jiandu owns memory persistence, lexical retrieval, and derived Dream snapshot bytes. Dream is orientation material, not canonical truth. No duplicate Bamboo memory index or embedding pipeline is required.
- Keep the service on loopback. A fresh instance is unauthenticated, and private-LAN peers are treated as trusted-local by the current server even when a password is set. For remote access, keep loopback binding/publishing and put an authenticating reverse proxy in front on a trusted network.
- The Docker Compose configuration publishes
127.0.0.1:9562:9562, uses a non-root user and named data volume, and drops capabilities. Runcd docker && docker compose up -d --buildto build that setup. Provider setup is still required. - Terminal, server, and browser interfaces do not themselves supply native desktop control. Nova is a separate MCP capability with its own platform and permission requirements. This documentation audit is not a macOS/Windows runtime certification.
Where Bamboo fits
flowchart LR Bodhi["Bodhi · desktop shell"] --> Bamboo["Bamboo · local agent runtime"] Lotus["Lotus Next · browser UI"] --> Bamboo CLI["Terminal / HTTP clients"] --> Bamboo Bamboo --> Provider["Configured model provider"] Bamboo --> Jiandu["Jiandu · canonical memory"] Bamboo --> MCP["MCP tools · e.g. Nova"]
Loading
Bodhi starts and health-checks its owned Bamboo sidecar. External-server reuse is limited to the explicitly selected legacy rollback path. In this source revision, Bamboo's default embedded frontend is Lotus Next, not the legacy Lotus UI. The optional bodhi-server account/provider service is not required for the local path. Magpie connects messaging channels; Pavilion hosts the website/docs. See Zenith for the complete module map.
The Cargo workspace has four tiers: crates/core (types/interfaces), crates/infra (storage, providers, memory, MCP, permissions and other services), crates/engine (agent loop/tools), and crates/app (server, SDK, TUI, broker and client). The root bamboo binary composes these; source-level actor/broker commands are advanced entry points, not a promise of unlimited concurrency or release maturity.
Develop and explore
cargo fmt --check
cargo test
cargo clippyBare Cargo commands use the manifest's default-members; cargo test is not every workspace member. The dev-only analytics crate is excluded by default. Inspect Cargo.toml before using --workspace.
- Architecture · Configuration · Skill bundle input
- Plugins · Migration · Documentation index
- Contributing · Changelog · Security
Ordinary Instruction publications also retain a private source binding. The main file and invocation-policy sidecars are captured through the same bounded source capability and parsed once. Raw edits, physical file/root replacement, and policy presence changes invalidate the publication even when normalized metadata is equal. Captured policy bytes must agree with the auxiliary snapshot; read errors and links cannot become an absent policy.
Source roots share a bounded handle pool across mode, Project and workspace stores. Temporary walks and old publications remain charged while referenced; Invalid/LKG entries and failed refreshes cannot grant future progressive API access. Public catalog serde, legacy Workflow adapters and deterministic orchestration keep their existing interfaces. The source binding itself adds no caller permission, activation, or runtime registration.
The portable source fixtures run in the existing manual/promotion Build matrix. Windows uses cap4.0.3 opened-handle identity with checked by-handle values; a candidate requires real Windows/Linux/macOS fixture results before portable acceptance. Ordinary eager instruction/resource storage remains until #1563.
bamboo_skills::progressive exports a source-validated Instruction catalog. bamboo_server_tools::SkillsListTool
exports a paged metadata Tool requiring a trusted caller/current-input resolver.
Known host ceilings distinguish None, empty and populated sets; stale UI
selection cannot grant manual invocation. Pages charge ToolResult and provider
cache envelopes, advance to metadata EOF, or return an explicit budget error.
The byte ceiling bounds each page-bearing block; unrelated request history is
outside this per-page budget.
SkillsListTool::render_catalog uses the same fresh metadata projection and the
pinned Codex allocator: a 2% context budget, an 8,000-character fallback, or an
independent configured token cap. Names, locators, root aliases and omission
notices consume that budget; descriptions share remaining space round-robin.
The complete defaults-backed SDK ergonomic execution surface installs these Tools with a
private current-run/current-User resolver. Server, deployed worker and other runtime
assembly surfaces still require their separate atomic integration.
Engine's session_app::skill_input::prepare_skill_input is a pure
ordinary User-content converter used by the SDK host factory. The host must supply
current-input restrictions
and correlated typed selections/snapshots; catalog, configured IDs and client
fragment text establish no invocation. Explicit bodies use an 8,000 UTF-8-byte
limit with visible warnings; bounded arguments are rejected rather than cut.
session_app::plan_legacy_skill_history prepares loaded-only ordinary Assistant
history after the complete original tool batch. It validates typed historical
data and a unique successful active receipt, retains originals and uses the
existing 512 KiB bound. Unsupported history stays intact. Neither helper is
called by chat. The defaults SDK invokes input conversion through its host factory and
converts supported loaded Instruction history during runner setup, checkpointing before
the obsolete outbox can run.
bamboo_server_tools::SkillInputFactory is a host preparation entry
point for an actual User that has not been appended. It requires a freshly
resolved caller and typed current selections; host ceilings, disabled/manual
policy, Root Ultra and Project/workspace scope remain separate restrictions.
New Sessions require explicit host provenance and first/final absent storage
rows. Existing Sessions reuse their persistence owner before publication guards
and a final direct fallible storage read. Definition, schema, mode and Source
are borrowed from one current publication, with charged raw/physical validation
before rendering and before success. Only ordinary Message data and warnings
return; no Session, pin or reader permission is written. The SDK async typed entry
borrows its actual repository owner and uses this factory before final append. The
subsequent existing checkpoint verifies the exact final User before exposing bounded
current-input data to metadata and Reader dispatch.
Its prepare_input_with_owner entry borrows the host's active persistence guard
and checks the same Session and coordinator map before reading. It compares the
complete durable checkpoint separately from the request-local candidate; only
hook observations and the prompt precheck may differ. Runtime permission fields,
identity, transcript and host authority must remain equal. The borrowed entry
uses a synchronous resolver that denies by default and does not reenter the
Session or Config owner. Store refresh may run under the borrowed Session owner;
it takes no host Config lock or Session callback and retains no publication guard.
Both entries acquire retained Config after Session and before publication, then
validate current caller revocation after the final await. The SDK's
private User construction and append helpers retain one exact message ID/time
and all four wrappers' original synchronous append, lease and error order.
The SDK connects those preparations to its actual runtime and Reader; HTTP and other
host surfaces retain their existing paths.
The runner's existing Instruction activation path is factored into a private,
stateless legacy_instruction adapter. Outside the defaults SDK it still publishes the
selected pin,
requires one model-issued load_skill call, suppresses first-round answer text,
and refreshes the existing repository activation metadata before continuation.
Durable workflow context, resume behavior, terminal degraded results and
WorkflowRun ordering retain their existing contracts. The adapter adds no
caller grant, Session field, source reader or lifecycle writer.
The defaults SDK disables this adapter for its whole execution, including prompt projection, required-first-call, answer suppression, silent-stream and retrieval gates. Supported loaded Instruction history converts one way through the existing checkpoint; unsupported history remains intact and inert. Deterministic WorkflowRun ordering remains unchanged. Server, child and deployed-worker cutover, official legacy Tool/export retirement and the complete #1595 integration remain outstanding.
Chat's existing typed Instruction selection uses a private legacy_selection
adapter. Candidate revisions and snapshots still come from the same Skills
resolver, with isolated staging pins and the existing metadata checkpoint.
Hooks, images, Root modes and input admission remain in the chat handler.
The final selected-input commit retains the original persistence and runners guards through durable save, admission and pin handoff. It remains detached from response cancellation; those guards are released before activation. Ordinary requests keep their existing input and idempotent replay behavior. This adapter adds no caller grant, Session field, reader registration or additional writer. These HTTP paths do not use the SDK host preparation entry.
Native chat and queued HTTP input use private constructors for the same User Message and inbox envelope. Native chat retains its real ID, timestamp and nondeduplicated attachments; the existing consumer checkpoints its User and pending handoff. Queued input retains authenticated admission and its durable retry identity. The four fresh-input SDK wrappers share one synchronous append helper at their original call positions, including synchronous stream creation. Session-only execution and resume retain their supplied history. These helpers preserve the existing public and serialized layouts. The defaults SDK now connects its actual append to checked current-input data and Reader execution; HTTP producer helpers alone provide no Skill authority.
License
Project-owned code is licensed under the MIT License. Third-party materials retain their own licenses and copyright notices:
builtin_skills/skill-creatorretains its Apache-2.0 license.- Codex-derived tool-search, Skill-input and catalog/list/render code retain its Apache-2.0 license and source notices.
The exported Rust SkillsListTool::selected_source helper applies the same mandatory
current-caller/input, host ceiling, config and source validation as list/render.
It returns complete raw UTF-8 SKILL.md or a published auxiliary file (up to8MiB),
with byte/entry/inflight limits shared by a manager's stores. Owned data remains
charged through the last real owner and carries no future execution permission.
probe_selected_source rechecks current authority and raw/physical identity with
bounded charged scratch. These APIs do not register a skills_read Tool or provide
paging/cache/runtime activation. Existing publication storage has separate bounds.
The exported SkillsReadTool, installed on the defaults SDK ergonomic surface, reuses
SkillsListTool's mandatory
trusted caller resolver. Reads use stable packages plus SKILL.md or a published
relative resource. Follow next_cursor to complete EOF before applying instructions.
A finite owned snapshot cache retains shared byte charges through active borrows;
every continuation validates current caller/input, host policy and source identity.
UTF-8 pages charge the largest real OpenAI Chat/Responses, Anthropic cache
(including 1h), Gemini page-bearing block and complete ToolResult envelope.
render_skill_usage_instructions supplies budgeted guidance to the actual SDK prompt.
The provider must follow main-resource pages to EOF before task actions; referenced
resources remain on demand. The finite run owner revokes access on cancellation, drop
and completion, even when Tool or host clones remain.
skill_response_byte_budget is a scalar budget helper. The SDK Reader resolver composes
its actual current dispatch output cap with the
existing response byte ceiling. Unknown caps and zero response bytes fail;
a known zero token cap means no hard token cap while retaining a finite512KiB
byte ceiling. Positive caps conservatively limit response bytes to that cap.
The helper grants no Skill access and changes no generic compressor behavior.
Impossible complete envelopes fail through the existing ToolError path rather
than emitting partial successful JSON or claiming EOF. Their plain failure text
has no successful-page token-bound promise. Test-owned Reader overlays exercise
this composition through the actual Runtime and outbound provider converters;
they do not install a production Reader or establish configured-provider access.
Engine observes the actual Session's output cap after continuing BeforeTool
hooks and scopes that scalar to the same executor future and exact Session/call
IDs. Generic compression receives that same captured value. A known zero keeps
its finite Reader envelope; an unknown value is not replaced with a saved or
model-name-derived default. Pre-dispatch blocks and synthesized timeouts retain
the original local compression projection and carry no retained observation.
scope_tool_output_cap and observed_tool_output_cap support same-future
in-process composition without changing public context, Session or SDK layouts.
The scope is host-constructible data, never a caller or Source permission grant.
A genuine Reader still performs fresh caller, input, ceiling, configuration,
Session, Source and cache validation on every page. Inline forwarding preserves
matching context; nested new-call IDs, SDK approval replay, opaque/no-context
executors and remote transports do not acquire a cap from old Session history.
Unwrapped spawned/blocking tasks and detached completions do not inherit it.
Moving a whole scoped future preserves its own per-poll observation, and scope
exit, cancellation and unwind restore the caller's previous scope.
Defaults SDK ergonomic execution now connects Reader registration, actual current-input transport, finite trusted caller resolution and output-cap composition. Server, child and deployed-worker integration remains pending.
Server and deployed workers construct the existing load_skill and
read_skill_resource overlays through assemble_legacy_skill_tools in
skill_runtime/assembly.rs. Server retains its Project store and the actual
permission-checked pre-Skill context registry; workers retain their absent
optional adapters. Strict-native workers bypass this construction. The defaults
SDK installs progressive catalog/read Tools on its ergonomic execution surface;
its legacy Instruction execution adapter is inert. The old classes remain
exported for the other host surfaces, whose legacy behavior remains unchanged.
Their integration and official export retirement remain part of #1595.
Canonical User envelopes can retain bounded, untrusted Skill request data in
SessionMessageContent::skill_request. A current HTTP workflow_selection
supplies one exact id/source/revision/args selection with no mode. Existing
queued and Root envelopes preserve this data and include it in retry identity;
the existing native Message path and queue admission conditions are unchanged.
This carrier does not prepare or invoke a Skill, authorize a source/body read,
or identify a historical message as current input. Fresh caller, current User,
Source, schema, configuration and policy checks remain mandatory at eventual
use. The existing Workflow/Instruction activation path remains live.
Absent request data keeps legacy JSON, .text() construction, provider
text/parts and canonical proof/idempotency bytes compatible. The new optional
public field intentionally changes Rust struct-literal construction: existing
SessionMessageContent { text, parts } callers must write
SessionMessageContent { text, parts, skill_request: None }. Data bounds limit
request shape and size; they do not guarantee admission under the existing
whole-envelope Inbox limit. Guidance, peer messages and child/runtime
presentation cannot turn this data into a fresh User request.
Execution wrappers can carry a separately owned UntrustedExecutionInputs
parameter into the execution-private config. HTTP checked Native and queue admission
supply only this call's newly committed User IDs after ACK succeeds; SDK
run, run_with_cancel, run_stream and run_stream_cancellable supply the
exact User each just appended, with no request derived from its text.
Old session/resume/custom execute/spawn entrypoints default to None.
The public SessionExecutionArgs, ExecuteRequest and Server spawn argument
layouts remain unchanged. At most 128 ID/request records are retained, each
request bounded by the existing I-W rules; this transport is separate from the
later aggregate projection cap and does not classify inputs as current.
Admission/startup failure drops the local data; transcript recovery cannot
mint it again. No message/images, Skill bodies or Source authority objects
are retained. This remains unwired caller data, without preparation, Reader
registration, resource reads, grants or a live Skill cutover. Native nonqueued
Chat uses the same checked Inbox handoff; NoNew and history cannot reconstruct it.
A separate unwired Engine helper can project borrowed request records into one bounded, untrusted batch. It checks all original I-W request data, then charges one private compact view including exact session/execution/input IDs, canonical source/kind/wrapper provenance, original timestamps and explicit absent requests. The whole batch is limited to 128 records and 256 KiB of compact UTF-8 bytes; record/selection slots are bounded separately. Owned data copies only validated lengths, without retaining Message/image/Source objects or source capacities. The helper establishes no New/current-input evidence, publication or permission; queued observation, execution transport and live Skill cutover remain separate.
Checked queued admission can now supply execution-local current input data after the existing transcript checkpoint and exact ACK succeed. Unknown, failed or oversized observations clear prior data; a successful boundary with no new input retains data only within the same live session and execution. A new ordered batch replaces the old IDs and selections, including when its request is absent. Overflow preserves the original admission, events, memory and provider behavior.
The main loop owns this finite value and drops it at every terminal return. The optional Lifecycle companion returns data to its caller; old implementations run once and return unavailable data. The initial checked HTTP handoff is sealed and consumed once under the same measured execution UUID. Its existing I-E compatibility slice adds bounded slots and args AST storage; the 256 KiB compact cap is not a heap/RSS bound. Generic SDK/history constructors do not create current-input evidence. The defaults SDK's actual append reserves a private execution identity and exposes input data only after the exact durable User is verified. Failed, absent or changed durable reads clear the SDK data; a successful checked NoNew boundary can retain it within that same execution. Resume and run_session cannot recover current invocation from history. The actual provider/retry/Tool future borrows the uniquely owned Q value, and normal completion returns that same owner before the next boundary. Host selection remains a ceiling, and every Skill use still requires fresh caller, Source, schema, configuration and policy checks. This SDK integration does not complete #1595 or the overall migration.
Sub-agent depth
subagents.max_spawn_depth controls new child creation. Root is depth 0; the
default of 4 permits four child levels, and 0 disables new child creation. The
Host derives each child's depth from the durable parent chain for SubAgent,
Plan and other child creation entry points. Tool arguments cannot select a
parent depth or reset the tree root.
Reloading this setting affects later creation attempts without rewriting existing sessions. A lower cap does not cancel existing children or hide their history. Worker tool exposure is fixed when the worker is provisioned; after raising the cap, newly provisioned workers receive the new nesting capability. Existing workers that were provisioned at their former cap keep their original tool surface. Remote workers use the same Host creation checks.
来源:GitHub · github.com

