Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Migration to 0.14

Every name deprecated in SDK 0.14, its replacement, and what 1.0 removes.

0.14 is additive: every 0.13 call keeps working and there is no wire change, with one exception in Rust. Client::lessons() is now the zero-argument lesson namespace, so the 0.13 flat client.lessons(payload) could not stay and 0.13 Rust code that calls it does not compile. Use client.lessons().list_legacy(payload), which reaches the same operation. Each deprecated name prints one warning per call site with a link to its section on this page. 1.0 removes the deprecated names. To find every use before you upgrade, run your test suite in strict mode:

python -W error::mubit.MubitDeprecationWarning -m pytest   # Python
MUBIT_DEPRECATIONS=error node app.mjs                      # JavaScript

Rust: add #![deny(deprecated)] to your crate root. mubit doctor prints the number of distinct deprecation warnings emitted in the current process.

Entry points: learn and auto

0.130.14Notes
mubit.learn.init(api_key=..., agent_id=...)mubit.init(api_key=..., agent=...)learn.init stays as an alias and prints one warning
mubit.auto.instrument()mubit.init(instrument=True) or mubit.init(instrument=["openai", "anthropic"])
mubit.auto.wrap_openai(client), mubit.learn.wrap(client)mubit.wrap_openai(client), mubit.wrap_anthropic(client), mubit.wrap(client)Wrappers are top-level
mubit.learn.uninstrument()mubit.uninstrument()
learn.feedback({ good: true }) (JS)mubit.outcome({ good: true })The 0.13 call crashed with reading 'slice'; it now delegates to outcome and returns an OutcomeReceipt
learn.init(auto_extract=True, extraction_mode="heuristic")no equivalentLessons are distilled on the server at run end under the policy; client-side extraction options are accepted and ignored with a warning
@mubit.learn.run(agent_id="planner")@mubit.agent("planner")
JS learn.withRun(opts, fn), learn.startRun(...)mubit.run(name, fn, opts)
Rust learn::LearnSessionclient.run(name).start() + run.context() / run.model_call() / run.end()LearnSession stays as an alias

JavaScript imports and option names

0.130.14Notes
import mubit from "@mubit-ai/sdk/init"import mubit from "@mubit-ai/sdk"Root export carries init, the wrappers and every namespace; /init, /loop, /control, /wrap, /learn, /helpers, /context subpaths remain as aliases until 1.0
require("@mubit-ai/sdk") without init / learnCJS entry now exports both
snake_case option keys ({ api_key, session_id, agent_id })camelCase ({ apiKey, runId, agentId })snake_case accepted with one console.warn; TypeError in 1.0
client.remember({ idempotency_key }) silently ignoredclient.memory.remember(content, { idempotencyKey }) honouredBug fix
init(): Promise<unknown> (async, creates a session)init(): Promise<MubitContext> — still async: it probes /v2/loop/capabilities and, unless you pass createSession: false, creates a session. Python's init() is the synchronous one.See init() and sessions
mubit.end() after manual runsawait mubit.run(name, fn) ends the run; await mubit.shutdown() when you manage runs yourselfbeforeExit does not fire on process.exit()

mubit.init() options

init takes keyword (Python) or object (JS) options only. Positional parameters and the alias pairs are accepted with a warning until 1.0.

0.130.14
mubit.init("mbt_...", "https://...")mubit.init(api_key="mbt_...", endpoint="https://...")
agent_id= (Python)agent=; JS keeps agentId: (agent: is accepted as an alias with a warning)
project_id= / projectId:project= / project:
user_id= (Python)user=; JS keeps userId: (user: is accepted as an alias with a warning)
JS sessionId:runId:
auto_instrument= / autoInstrument:instrument= / instrument: (True, False or a list of library names)
auto_learn= / autoLearn:inject=InjectOptions(enabled=...)
fail_open=on_error= (see Failure policy)
create_session= (new in 0.14, default True)opt in to create_session=False now; False becomes the default in 1.0

init() and sessions

In 0.13 init() created a server session and registered an exit hook that closed it with reflect_on_close=True. In 0.14 you can opt out with mubit.init(create_session=False) (JS { createSession: false }); in 1.0 this is the only behaviour. With no session, MubitContext.session_id is None unless you open a run with mubit.run(thread=...), and runs are created lazily on the first event. Run ends are emitted by mubit.run() scopes; there is no reflect-on-exit network call.

context() is a pure read

In 0.13 calling mubit.context() inside a run switched the run to manual injection, so wrapped model calls stopped injecting. In 0.14 the switch still happens but prints a warning; in 1.0 context() has no side effect. Say what you mean when you open the run:

with mubit.run("invoice-format", inject="manual") as run:   # wrappers do not inject; you place ctx.text
    ctx = run.context(task)

@mubit.agent("planner", inject="manual") does the same for decorated functions. JS context({ manual: true }) is kept for one minor.

Default endpoint

mubit.init() and Client() default to https://api.mubit.ai; the CLI and the deprecated learn path default to http://127.0.0.1:3000. 0.14 prints one warning whenever a local default is used without MUBIT_ENDPOINT, and one when env resolves to default implicitly. Set MUBIT_ENDPOINT and MUBIT_ENV explicitly (the console's four-line .env block does this); 1.0 uses one default for every entry point.

Scope names: session_id, thread, visibility

session_id meant three things in 0.13: the run id on the memory helpers, the conversation id on events, and the server session on MubitContext. 0.14 names each one.

0.130.14Notes
client.remember(session_id=...), recall(session_id=...), get_context(session_id=...), archive(session_id=...), dereference(session_id=...)run_id=session_id accepted as an alias of run_id on the memory helpers until 1.0
mubit.run(session=...) / mubit.run(session_id=...)mubit.run(thread=...)The conversation id carried on events; the wire field stays session_id for one major
Run.session_idRun.thread
lesson_scope="global", share=Truevisibility="global"One field: run, agent, project, global
client.lessons(session_id=...)client.lessons.transitions(run_id=...)0.13 raised TypeError; 0.14 warns (lessons(session_id=) is not a filter on the lesson set) and forwards to transitions; 1.0 raises
Rust RunBuilder::session(...)RunBuilder::thread(...)
Client.run() legacy scope argumentrun_scopeRemoved in 1.0

Resolution order per field: explicit argument → with_options(scope=...) / mubit.scope(...) → active run or step → init / Client default scope → environment. See SDK configuration → Scoping.

Client construction

0.130.14
Client(endpoint=...) then client.set_api_key(key) / client.setApiKey(key) / set_token(key)Client(api_key=key, endpoint=...); Client() reads MUBIT_API_KEY and MUBIT_ENDPOINT
Client("https://...") positionalClient(endpoint="https://..."); the positional form is accepted until 1.0
run_id=, agent_id=, user_id= constructor optionsscope=Scope(run_id=..., agent=..., user=...) or client.with_options(scope=...)
Client(timeout_ms=..., retry_attempts=...)Client(timeout=30.0, connect_timeout=2.0, max_retries=2); JS timeoutMs, connectTimeoutMs, maxRetries
Rust ClientConfig::from_env() / ClientConfig::new(endpoint)Client::from_env() / Client::builder()

Flat client methods and client.advanced

The 0.13 client exposed every control operation directly on Client and, from 0.13.1, under client.advanced. 0.14 groups them into namespaces; the flat names and client.advanced.* are aliases that print one warning each, and client.raw.invoke("control.<op>", payload) reaches any operation by its contract name. The #<op> fragment in each warning (/sdk/migration#query) lands on the row for that operation below.

0.130.14
client.query(...), client.advanced.query(...)client.memory.recall(query, ...); wire-level fields (budget, min_timestamp, rank_by, explain) through client.raw.invoke("control.query", {...})
client.ingest(...), client.advanced.ingest(...)client.memory.remember(...) for one item; client.raw.invoke("control.ingest", {...}) for batches and raw item fields
client.get_context(...) / getContextclient.memory.context(task, ...); lane="legacy" for the /v2/control/context assembler
client.lessons(as_of=...) (method)client.lessons.list(as_of=...)
client.reflect(session_id=...)client.lessons.reflect(run_id=...)
client.record_outcome(...) / recordOutcomeclient.outcomes.record(...); mubit.outcome(...) for the current run
client.record_step_outcome(...) / client.recordStepOutcome(...)client.runs.steps.record(...)
client.feedback(...)client.outcomes.feedback(...) or client.agents.handoffs.feedback(...)
client.checkpoint(...)unchanged: a typed top-level method (context_snapshot, label, session_id, agent_id, metadata); client.snapshots holds lesson-set snapshots, not run checkpoints
client.get_ingest_job(...), client.advanced.get_ingest_job(...) / client.advanced.getIngestJob(...)client.jobs.get(job_id)
client.get_run_ingest_stats(...)client.raw.invoke("control.get_run_ingest_stats", {...})
client.advanced.list_run_history(...) / client.advanced.listRunHistory(...)client.runs.history(...)
client.link_run(...), unlink_run, delete_runclient.runs.link(...), client.runs.unlink(...), client.runs.delete(run_id)
client.advanced.context_snapshot(...) / client.advanced.contextSnapshot(...)client.raw.invoke("control.context_snapshot", {...})
client.register_agent(...), list_agents, handoffclient.agents.register(...), client.agents.list(...), client.agents.handoffs.create(...)
client.memory_health(), diagnose, forget, archive, archive_block, dereferenceclient.memory.health(), diagnose, forget, archive, dereference
client.surface_strategies(...)client.lessons.strategies(...)
client.create_project(...), get_project, …client.projects.create(...), get, update, delete, list
client.set_prompt(...), activate_prompt_version, get_prompt_diff, get_prompt, list_prompt_versionsclient.raw.invoke("control.set_prompt", {...}) etc.; client.optimize_prompt(agent_id=..., project_id=...) stays a typed top-level method
client.create_skill(...), list_skills, get_skill, update_skill, delete_skill, list_skill_versions, activate_skill_version, get_skill_diffclient.raw.invoke("control.create_skill", {...}) etc.; client.optimize_skill(skill_id=..., project_id=...) stays a typed top-level method
client.kill(True, "reason"), client.kill_status()client.kill.set(on=True, reason="reason"), client.kill.get()
client.export.skills(...), client.export.audit(...)client.audit.export_skills(...), client.audit.list(...)
client.list_activity(...), export_activityclient.audit.run_history(...)
client.subscribe(...), watchclient.audit.subscribe(...), client.audit.watch(...)
client.auth.*client.admin.users.*, client.admin.api_keys.*, client.admin.permissions.*
client.core.*client.raw.invoke("core.<op>", payload)
any other client.<op>() / client.advanced.<op>()client.raw.invoke("control.<op>", payload); the warning text names the op

Rust: the 55 flat delegates on Client are marked #[deprecated]; the replacements are the accessor methods (client.memory(), client.runs(), client.lessons(), …) and client.raw().invoke(op, value).

Keyword-only control arguments

Positional scope and boolean arguments on the control namespaces are accepted with a warning in 0.14 and rejected in 1.0.

0.130.14
client.kill(True, "maintenance")client.kill.set(on=True, reason="maintenance")
client.snapshots.restore(id, True, False, True)client.snapshots.restore(id, lessons=True, policy=False, retire_new=True)
client.policy.get("billing", "prod")client.policy.get(project="billing", env="prod")
Rust client.kill_set(true, "maintenance")client.kill().set(KillRequest { on: true, reason: "maintenance".into() })

Outcome arguments

One normaliser applies to mubit.outcome, run.outcome, step.outcome, mubit.aio.outcome, JS Step.outcome and Rust OutcomeRequest: a bool is good, a number is score, a string is label.

0.130.14
mubit.aio.outcome(0.8, outcome_label="resolved")mubit.aio.outcome(score=0.8, label="resolved") (positional form accepted with a warning)
step.outcome(0.5) sent name=0.5step.outcome(0.5) sends score=0.5 (bug fix)
mubit.outcome(good=False, name="corrected_by_user")mubit.outcome(good=False, label="corrected_by_user"); name= accepted as an alias

Failure policy

fail_open booleans on init, learn.init, LoopClient, LearnConfig and LoopConfig are aliases of one on_error setting.

0.130.14
fail_open=Trueon_error="warn" (default for the global helpers)
fail_open=Falseon_error="raise" (default for Client)
silent logger.debug("... (non-fatal)") on the loop pathone WARNING per endpoint and failure class; MUBIT_LOG=error silences
helpers before init(): silent no-op for run/step/note, RuntimeError for outcome/contextauto-initialise from env with one WARNING; ConfigurationError only when no key can be resolved. Code that relied on the silent no-op sets MUBIT_DISABLED=1

Errors

The 0.13 exception names are aliases of the 0.14 hierarchy and are removed in 1.0. Every APIStatusError carries status_code, request_id, body and retry_after.

0.130.14
AuthErrorAuthenticationError (401)
ValidationErrorBadRequestError (400)
AlreadyExistsErrorConflictError (409)
ServerErrorInternalServerError (500)
TransportErrorAPIConnectionError (.code kept)
ControlErrorAPIStatusError
ControlUnavailableAPIConnectionError
except ServerError for every non-2xxRateLimitError (429) and GoneError (410) have no 0.13 parent; catch them explicitly
server-status classes raised for client-side argument problemsValueError / TypeError (Python), TypeError (JS) in 1.0

except AuthError still catches a 403 (PermissionDeniedError is also an AuthenticationError), except ValidationError still catches 404 and 409, and except ServerError still catches 503; the full mapping is in Errors.

Environment variables

0.130.14
MUBIT_LOOP_DISABLED=1MUBIT_DISABLED=1 (whole SDK, not only the loop lane)
MUBIT_LOOP_DEBUG=1MUBIT_LOG=debug
MUBIT_RETRY_ATTEMPTS=3MUBIT_MAX_RETRIES=2 (attempts = retries + 1)
MUBIT_PROJECT_IDMUBIT_PROJECT
MUBIT_TOKEN (Rust)MUBIT_API_KEY
MUBIT_LEARN_EXTRACT, MUBIT_LEARN_CONTEXT_TIMEOUT, MUBIT_LEARN_ATTRIBUTION_TIMEOUT, MUBIT_CONTROL_REVIEW_ENABLED, MUBIT_CONTROL_PROD_VERIFIED_BOOSTmubit.init(inject=InjectOptions(...), capture=CaptureOptions(...))

New in 0.14 (no old name): MUBIT_AGENT, MUBIT_USER, MUBIT_ON_ERROR, MUBIT_TIMEOUT_MS, MUBIT_CONNECT_TIMEOUT_MS, MUBIT_CONSOLE_URL. The full table is in SDK configuration.

Capture lane for the Python patch path

In 0.13 the Python autopatch sent captured turns to /v2/control/ingest. In 0.14 you can opt in to model.call loop events with mubit.init(capture=CaptureOptions(lane="loop")); the wrappers (wrap_openai, wrap_anthropic) already use the loop lane. 1.0 makes the loop lane the default on servers that have /v2/loop.

CLI

0.130.14
mubit status --agent-id x (error)mubit status --agent x; --agent-id accepted as an alias
global options only before the subcommandaccepted before or after (mubit doctor --api-key ... works)
doctor probed /healthz (404)probes /v2/core/health
new: mubit runs list, mubit runs tail, mubit runs open, mubit policy diff, mubit init, mubit login, mubit doctor --json

What 1.0 removes

  • The 41 Python flat shims, the 41 JS flat shims, the 55 Rust deprecated delegates, and client.advanced.
  • mubit.learn.init, mubit.auto.instrument, learn.wrap*, set_api_key / setApiKey / set_token.
  • session_id aliases, lesson_scope / share, fail_open aliases, positional init parameters, positional control-namespace arguments.
  • MUBIT_LOOP_DISABLED, MUBIT_LOOP_DEBUG, MUBIT_PROJECT_ID, MUBIT_RETRY_ATTEMPTS, MUBIT_TOKEN.
  • JS subpath exports and snake_case option keys.
  • Client.run() legacy scope argument (use run_scope).
  • The implicit manual-injection switch in context(); the session created by init(); the local default endpoint outside MUBIT_ENDPOINT.

The 0.13 exception aliases and the dict-style access on typed responses (answer["final_answer"]) stay for the whole 1.x line.

Upgrade checklist

  1. pip install "mubit-sdk>=0.14,<1" / npm install @mubit-ai/sdk@^0.14 / mubit-sdk = "0.14".
  2. Run the test suite in strict mode (top of this page) and fix each warning using the tables above.
  3. Set MUBIT_ENDPOINT, MUBIT_PROJECT and MUBIT_ENV in every environment; run mubit doctor and check its scope: line.
  4. Replace fail_open with on_error, and read the reason field on ContextBlock and OutcomeReceipt where you used to check for None.
  5. Move session_id to run_id or thread, and lesson_scope to visibility.