[0.8.3] - 2026-09-25
Added
- Web vs Android public-behavior inventory.
docs/web-android-public-behavior.md
classifies remaining public backend splits (notes tombstone projection, raw
escape-hatch types, deliberate research-import policy, tracked default flips
gated by C3-02/C5A-01, and the #2384
get_historycontract fix) so callers
do not treat every difference as a defect. - Owner-grouped client configuration.
notebooklm.optionsadds frozen
ClientConfiggroups for runtime capacity, retries, selected backend,
transfer phases, feature read windows, and Web session hooks. Both direct
construction andfrom_storageaccept keyword-onlyconfig=while retaining
flat 0.x arguments through one compatibility normalizer. - Public mutation-outcome evidence.
notebooklm.outcomesnow exposes
CommitState,RecoveryAction, frozenOperationMetadata, typed reconciliation reports,
and ordered batch outcomes. These let callers
distinguish a request proven not sent, a decoded rejection, an unknown
commit outcome, and a caller-correlated confirmation. Unknown-outcome errors
retain the compatibilityunconfirmedmarker and may expose bounded
reconciliation candidates for manual inspection. Legacyunconfirmed,source_id, and
stageattributes remain projections; because they are now declared onNotebookLMError,
callers should testsource_id is Nonerather thanhasattr(error, "source_id"). - Aggregate operation deadlines and journals. Public
client.operation(timeout=...)andRuntimeOptions.operation_timeoutapply
one monotonic budget across queueing, authentication, retries, transport,
polling, reconciliation, and transfers. Owned expiry raises
OperationTimeoutError; partial failures and cancellation retain the
operation's ordered mutation evidence. - Adapter-neutral preparation and execution. CLI, MCP, and REST workflows
now share presentation-free_apprequest preparation and execution cores.
Generation uses typed requests and outcomes, while source batches preserve a
typed result for every input occurrence. MCP confirmation previews return
canonical resource IDs; during v0.9, a successful confirmed mutation that
still uses a name or partial ID emits a registered compatibility warning. - Pre-merge live CI qualification. The repository owner can dispatch RPC health or nightly
E2E from the trustedmainworkflow against the immutable head SHA of an open, same-repository
PR targetingmain. Forks, non-owner dispatches, and direct feature-ref dispatches remain
blocked from protected credentials, while account selection and raw-token materialization remain
pinned to trustedmaincode and a trusted lockfile-built environment before candidate
dependencies execute. Nightly dispatches can also select only the full Web, Android, or read-only
live lane.
Fixed
-
Research import no longer hangs on unclosed Markdown links (#2434).
Citation URL extraction no longer triggers catastrophic regex backtracking
on long URLs in malformed Markdown links or images. Cited-only research
imports can process these reports without blocking the MCP event loop,
which previously caused connector and OAuth requests to time out. -
Artifact polling preserves unresolved absence (#2432). Repeated listing
misses no longer produce a terminalREMOVEDor fabricated quota error.
Waiters keep the original ID until completion, explicit failure, or timeout;
a completed sibling never substitutes for the requested artifact. Web listing
errors carried by null RPC responses now propagate, with bounded retries for
transient polling reads. The publicmax_not_foundand
min_not_found_windowarguments are deprecated and ignored; non-default
values warn. Permanently missing IDs now reach the caller's timeout. The
REMOVEDenum remains available for compatibility. -
MCP stdio
source_addhost-path file-add is default-deny.
source_add(source_type="file", path=...)over stdio now requires
NOTEBOOKLM_MCP_ALLOWED_ROOTS(OS-pathsep directories;$HOMEand
~/.notebooklmare rejected as roots). Known credential filenames
(storage_state.json,master_token.json) and Playwright profile dirs
are refused even inside an allowed root. Remote HTTP still never opens a
server-hostpath. -
Fuzzy artifact resolution refuses incomplete listings. CLI
resolve_artifact_idand MCPresolve_artifactnow require
list_with_status().is_completebefore title/prefix matching. A notes
outage (or other secondary backing failure) no longer turns an ambiguous
title/prefix into a unique hit or a partial miss into not-found; both
surfaces raise the existingartifacts.lookupincomplete-readRPCError.
Canonical UUID refs still fast-path without listing (#2380). -
REST/MCP research import shares one operation budget. Poll, optional
cited/max filtering, and import now run under oneclient.operationso a
configured aggregate deadline cannot restart between the read and the
mutation (Fixes #2382). -
CLI
ask --save-as-note --jsonkeeps save-failure commit evidence. Optional
note-save remains non-fatal, but JSON now projectsSaveNoteOutcome.failure
through the same commit-state / recovery-action / known-id fields as other CLI
errors instead of dropping them on a redactednote_save_errorstring (#2383). -
Retry-unsafe writes no longer replay after transmission. Notebook and
source creates, file registration, research imports, collection creates, and
chat POSTs now require explicit commit evidence before any outer replay.
Because neither backend's full-set response nor a matching list row proves
which collection belongs to the caller, Web and Android
client.collections.create()send once and then raiseCollectionErrorwith
bounded candidate IDs for manual identification. Staged Drive cleanup
deletes only rows proven to belong to the current operation. -
Web chat preserves ambiguous post-send outcomes. A transmitted turn is
never blindly replayed after an HTTP 429, 5xx, write/read/protocol failure, or
auth-shaped 400/401/403. Stale credentials are refreshed once for later
calls, but the current ask surfaces unknown commit evidence with
conversation-history inspection guidance. Positively pre-send connection
failures retain bounded retry. -
Decoded refusals stay distinct from transport throttles. Artifact
rate-limit retry now follows commit evidence rather than exception type: a
decoded service refusal withREJECTEDevidence may consume the explicit
retry budget, while a bare transport 429 or other unknown post-dispatch
outcome surfaces without replay. -
Nightly E2E and RPC health template validation. The disposable-copy contract now matches
the immutable public template title and its cross-backend copied artifact inventory, allowing
provisioning to proceed in the scheduled Web and Android lanes. Legacy quiz/flashcard rows that
Web cannot classify and absent optional artifact families no longer block provisioning; full E2E
still generates those families on disposable notebooks. Reference copies are polled for up to
ten minutes while inherited sources and artifacts finish processing; clean-role copies wait only
for ready sources before removing inherited artifacts. The lifecycle manager also uses the
public notebook as its default when no template override is configured. A close-time transport
failure after a one-shot lifecycle command has completed is now reported as a warning instead of
reversing the command's successful result. Read-only download checks now distinguish completed
artifact inventory from an actually downloadable asset, since public copies omit asset URLs. -
MCP server:
CONNECT_TIMEOUTon connect. The FastMCP lifespan opened the
NotebookLMClient— cookie rotation, the CSRF fetch, and the cold-recovery
ladder when those fail — before answering the MCPinitializehandshake.
That auth work's budget (a 15 sRotateCookiespoke plus a 30 s CSRF fetch,
more on the recovery rungs) exceeds the 30 s deadline hosts give the handshake,
so a slow or rate-limited Google surfaced to the host as a dead server
(MCP server notebooklm connection timed out after 30000ms) rather than an
auth error, and every retry spawned a fresh process that redid the same work.
The client is now opened lazily behind aClientProviderthat the lifespan
warms in the background: the handshake answers immediately, the first tool call
awaits the open, an auth failure arrives as a normal categorized tool error,
and a failed open is retried by the next call — so a mid-session
notebooklm loginrecovers a running server without a restart ([#2330]).
Changed
chat.get_historyraises on turn-fetch failures. Web previously logged
ChatError/NetworkErrorfrom the conversation-turns RPC and returned[],
so a failed fetch looked like an empty conversation. Android already raised.
Both backends now raise; for a positivelimit,[]means no conversation
or no turns. Android also returns[]for non-positive limits
(#2384).- Legacy client tuning has a v1 runway. A construction using non-default
flat tuning keywords now emits one caller-attributedDeprecationWarning
naming all arguments to migrate toconfig=ClientConfig(...). Explicit old
defaults remain silent. Web read/write/pool timers and start/finalize/Drive
transfer timers now reach their owning transports independently without
changing legacy defaults. - Create and research-import recovery is diagnostic, not success-producing.
The former probe-and-retry helper andPROBE_THEN_CREATEpolicy are retired.
Ambiguous mutations are sent once and surfaced unchanged.
research.import_sources_with_verification()never reissues an import after
response loss; its bounded read-only inspection can attach candidates and
unresolved inputs before re-raising, but cannot claim that a row belongs to
the failed call. - Artifact poll followers declare their v1 behavior. During v0.x the first
waiter still owns the shared leader's polling options, so differing follower
values are ignored with a deprecation warning. A follower
on_status_changecallback still receives only the final status and emits a
separate warning; v1.0 will make options per-waiter and deliver every
observed status to each callback. - Artifact download authentication failures now raise
AuthError. Public
artifact download methods surface HTTP 401/403 responses from guarded asset
transfers asAuthErrorinstead of wrapping them inArtifactDownloadError.
Callers that previously caught onlyArtifactDownloadErrorfor expired
credentials should also catchAuthError; other asset-transfer failures
continue to useArtifactDownloadError. This aligns the Web and Android
backends and lets CLI/MCP adapters return their structured authentication
error and login guidance. - Backend-conditional client assembly and backend-selected raw access. An
Android-selected client now constructs and owns only the shared and Android
runtimes during normal use: it no longer creates a Web Kernel, performs Web
cookie recovery/persistence, or starts Web keepalive. Useclient.raw.call(...)
on Web andclient.raw.unary(...)/unary_stream(...)on Android. The root
client.rpc_call(...)wrapper is deprecated in v0.9.0 for removal in v1.0;
during v0.x its first Android use lazily opens a no-keepalive Web compatibility
sidecar so mixed profiles keep their historical behavior. - Pull requests run a reduced 7-cell compatibility matrix again (Python
3.10–3.14 on Ubuntu plus Python 3.12 on macOS and Windows). The full 15-cell
Ubuntu/macOS/Windows × Python 3.10–3.14 matrix now runs only in the nightly
workflow, and manual nightly dispatches include it by default.
Documentation
- MCP/REST hosting threat model (
SECURITY.md). Operator-facing security
docs no longer claim the CLI has "no long-lived API keys or OAuth tokens".
They now document thatmaster_token.jsonis account-equivalent, MCP OAuth
refresh tokens are long-lived (rotatingNOTEBOOKLM_MCP_OAUTH_PASSWORDdoes
not revoke them), stdiosource_add(path)reads server-host files,/files/dl
and/files/ulare HMAC-URL auth only, open OAuth DCR does not bypass the
login password, MCP loopback HTTP may be tokenless while REST always requires
NOTEBOOKLM_SERVER_TOKEN,GET /healthzis liveness not readiness, and
pip-auditin CI still exportsbrowser+dev+markdownby default.
(#2387)
Removed
- Removed six deprecated private
_authcompatibility modules earlier than their documented
next-major removal window. Private importers must use_auth.cookie_filterinstead of
_auth._browser_cookie_filter,_auth.psidts_recoveryinstead of
_auth.browser_cookie_recovery,_browser.browser_captureinstead of
_auth.browser_state_validationor_auth.login_wait_trace,_auth.profile_storefor
transaction primitives instead of_auth.storage_transaction, and_auth.storageinstead of
_auth.storage_writer.