{"_id":"notebooklm-kit","_rev":"8-d075fecaa6d1674b45826749a8e3a149","name":"notebooklm-kit","dist-tags":{"latest":"2.2.0"},"versions":{"0.0.1":{"name":"notebooklm-kit","version":"0.0.1","keywords":[],"author":"","license":"ISC","_id":"notebooklm-kit@0.0.1","maintainers":[{"name":"photon-ai","email":"photon@something.surf"}],"dist":{"shasum":"7a728780b3e310a2afccfab572a54aad6a7a8c41","tarball":"https://registry.npmjs.org/notebooklm-kit/-/notebooklm-kit-0.0.1.tgz","fileCount":2,"integrity":"sha512-/VD295Zgwd276JUZneYzguC5eJ3T8TX7mEN1saUeKgQ6UhUUuiPbBv4LtVfoec/TngRPgv5pDUizpY2uA9jozw==","signatures":[{"sig":"MEQCIFdi3B+Qt/8381ASCJYneWuBQUamYAmFnHE2jH77FjgFAiADQx454Fwny7VUv2BQu5mil2WYeRLSrpf4WhwF7C5GPw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":322},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"photon-ai","email":"photon@something.surf"},"_npmVersion":"11.0.0","description":"Placeholder for notebooklm-kit","directories":{},"_nodeVersion":"20.18.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/notebooklm-kit_0.0.1_1766575451869_0.11234682229290271","host":"s3://npm-registry-packages-npm-production"}},"2.1.1":{"name":"notebooklm-kit","version":"2.1.1","keywords":["notebooklm","google","notebook","ai","research","sdk","typescript","artifacts","audio-overview","podcast","study-guide","flashcards","quiz","mind-map","report","infographic","slide-deck","slides","presentation","video-overview","video","generation","chat","sources","notes","automation","api-client"],"author":{"name":"photon-hq"},"license":"MIT","_id":"notebooklm-kit@2.1.1","maintainers":[{"name":"photon-ai","email":"photon@something.surf"}],"homepage":"https://github.com/photon-hq/notebooklm-kit#readme","bugs":{"url":"https://github.com/photon-hq/notebooklm-kit/issues"},"dist":{"shasum":"9109cd9b3f97794ddd18b3d06720cf39de8f8c71","tarball":"https://registry.npmjs.org/notebooklm-kit/-/notebooklm-kit-2.1.1.tgz","fileCount":99,"integrity":"sha512-3bWQgCJ9KTPdA+0R2DDX+0K8cCmcQYvnLLMzLxp2OkAAO9YUhofMpmDu4bc29t/Q4iEnvnFAVjU7gSehMHnqVA==","signatures":[{"sig":"MEUCIHWDzs/y/SIblk/vRo/04gQ4Pd459zd3AzPfZY9b575tAiEA2+hhFql6gLr1m8XY4thrfpCPjQCkWxwBirLE0PwzZoc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1378838},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"},"require":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"}}},"gitHead":"262932dab5c43bf49089520c069c2b1cbbaed33c","scripts":{"dev":"tsc --watch","build":"tsc","clean":"rm -rf dist","setup":"npm install && npx playwright install chromium && npm run build","type-check":"tsc --noEmit","postinstall":"npx playwright install chromium","prepublishOnly":"npm run clean && npm run type-check && npm run build"},"_npmUser":{"name":"photon-ai","email":"photon@something.surf"},"repository":{"url":"git+https://github.com/photon-hq/notebooklm-kit.git","type":"git"},"_npmVersion":"10.8.1","description":"TypeScript SDK for NotebookLM API","directories":{},"sideEffects":false,"_nodeVersion":"20.14.0","dependencies":{"dotenv":"^17.2.3","playwright":"^1.57.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","typescript":"^5.7.0","@types/node":"^22.0.0"},"_npmOperationalInternal":{"tmp":"tmp/notebooklm-kit_2.1.1_1767575663110_0.9461794388567875","host":"s3://npm-registry-packages-npm-production"}},"2.1.2":{"name":"notebooklm-kit","version":"2.1.2","keywords":["notebooklm","google","notebook","ai","research","sdk","typescript","artifacts","audio-overview","podcast","study-guide","flashcards","quiz","mind-map","report","infographic","slide-deck","slides","presentation","video-overview","video","generation","chat","sources","notes","automation","api-client"],"author":{"name":"photon-hq"},"license":"MIT","_id":"notebooklm-kit@2.1.2","maintainers":[{"name":"photon-ai","email":"photon@something.surf"}],"homepage":"https://github.com/photon-hq/notebooklm-kit#readme","bugs":{"url":"https://github.com/photon-hq/notebooklm-kit/issues"},"dist":{"shasum":"ac74a25b27bed7e7729647bbecc098b8377ec281","tarball":"https://registry.npmjs.org/notebooklm-kit/-/notebooklm-kit-2.1.2.tgz","fileCount":99,"integrity":"sha512-5yQOCHmR+UmmA9Pp3hlr0Ru5CePv1Y4ZPoqCpunbP95QzkPgPVgGcCy4KmcRU+2iPFBbg7LkXUPcUE4XkP9Nlg==","signatures":[{"sig":"MEQCIDNc0pJ2DDKXkZlRkhZ1kITCiJdEbKtKfHt+R9j3nv3iAiAMtd77D4szmgWbFZLy1ccLv+xudl31AVGm4JOuKKdOhQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1378817},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","config":{"commitizen":{"path":"cz-customizable"},"cz-customizable":{"config":".cz-config.cjs"}},"engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"},"require":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"}}},"gitHead":"54134758d9083db43b614a42517315460ec991fc","scripts":{"dev":"tsc --watch","build":"tsc","clean":"rm -rf dist","setup":"npm install && npx playwright install chromium && npm run build","commit":"cz","release":"standard-version","type-check":"tsc --noEmit","postinstall":"npx playwright install chromium","install-hook":"node scripts/install-hook.cjs","release:major":"standard-version --release-as major","release:minor":"standard-version --release-as minor","release:patch":"standard-version --release-as patch","prepublishOnly":"npm run clean && npm run type-check && npm run build"},"_npmUser":{"name":"photon-ai","email":"photon@something.surf"},"repository":{"url":"git+https://github.com/photon-hq/notebooklm-kit.git","type":"git"},"_npmVersion":"10.8.2","description":"TypeScript SDK for NotebookLM API","directories":{},"sideEffects":false,"_nodeVersion":"20.19.6","dependencies":{"dotenv":"^17.2.3","playwright":"^1.57.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","commitizen":"^4.3.0","typescript":"^5.7.0","@types/node":"^22.0.0","cz-customizable":"^7.0.0","standard-version":"^9.5.0"},"_npmOperationalInternal":{"tmp":"tmp/notebooklm-kit_2.1.2_1768027625783_0.054767332446423955","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"notebooklm-kit","version":"2.2.0","keywords":["notebooklm","google","notebook","ai","research","sdk","typescript","artifacts","audio-overview","podcast","study-guide","flashcards","quiz","mind-map","report","infographic","slide-deck","slides","presentation","video-overview","video","generation","chat","sources","notes","automation","api-client"],"author":{"name":"photon-hq"},"license":"MIT","_id":"notebooklm-kit@2.2.0","maintainers":[{"name":"photon-ai","email":"photon@something.surf"}],"homepage":"https://github.com/photon-hq/notebooklm-kit#readme","bugs":{"url":"https://github.com/photon-hq/notebooklm-kit/issues"},"dist":{"shasum":"9ce0e7e832cf855203e20278aa48ae7e16cddebf","tarball":"https://registry.npmjs.org/notebooklm-kit/-/notebooklm-kit-2.2.0.tgz","fileCount":99,"integrity":"sha512-aaQFTyI/cDMfD4KGPpQO4/3ysJ9m8Ehdvv5SE6ENZc1sk/sb2cb/1BPq0Hu54DNuS7Bwx7QS+R5+dFLsOKd/fQ==","signatures":[{"sig":"MEYCIQCTzrH3o6Kj2zEsvjTjswikkxbLvWwKjtGVGi9qHWNhVwIhAMPm8fNNGUphuYimdyFoNyblL/0gZzOEOI/JnTYk7Fo/","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1410453},"main":"./dist/src/index.js","type":"module","types":"./dist/src/index.d.ts","config":{"commitizen":{"path":"cz-customizable"},"cz-customizable":{"config":".cz-config.cjs"}},"engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"},"require":{"types":"./dist/src/index.d.ts","default":"./dist/src/index.js"}}},"gitHead":"3ced31e328bdc7b108be00d2be56261a24ec43b9","scripts":{"dev":"tsc --watch","build":"tsc","clean":"rm -rf dist","setup":"npm install && npx playwright install chromium && npm run build","commit":"cz","release":"standard-version","type-check":"tsc --noEmit","postinstall":"npx playwright install chromium","install-hook":"node scripts/install-hook.cjs","release:major":"standard-version --release-as major","release:minor":"standard-version --release-as minor","release:patch":"standard-version --release-as patch","prepublishOnly":"npm run clean && npm run type-check && npm run build"},"_npmUser":{"name":"photon-ai","email":"photon@something.surf"},"repository":{"url":"git+https://github.com/photon-hq/notebooklm-kit.git","type":"git"},"_npmVersion":"10.8.2","description":"TypeScript SDK for NotebookLM API","directories":{},"sideEffects":false,"_nodeVersion":"20.19.6","dependencies":{"dotenv":"^17.2.3","playwright":"^1.57.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.21.0","commitizen":"^4.3.0","typescript":"^5.7.0","@types/node":"^22.0.0","cz-customizable":"^7.0.0","standard-version":"^9.5.0"},"_npmOperationalInternal":{"tmp":"tmp/notebooklm-kit_2.2.0_1768360105999_0.8407472901938293","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-12-24T11:24:11.868Z","modified":"2026-07-11T06:16:31.483Z","0.0.1":"2025-12-24T11:24:12.041Z","2.1.1":"2026-01-05T01:14:23.306Z","2.1.2":"2026-01-10T06:47:05.947Z","2.2.0":"2026-01-14T03:08:26.157Z"},"bugs":{"url":"https://github.com/photon-hq/notebooklm-kit/issues"},"author":{"name":"photon-hq"},"license":"MIT","homepage":"https://github.com/photon-hq/notebooklm-kit#readme","keywords":["notebooklm","google","notebook","ai","research","sdk","typescript","artifacts","audio-overview","podcast","study-guide","flashcards","quiz","mind-map","report","infographic","slide-deck","slides","presentation","video-overview","video","generation","chat","sources","notes","automation","api-client"],"repository":{"url":"git+https://github.com/photon-hq/notebooklm-kit.git","type":"git"},"description":"TypeScript SDK for NotebookLM API","maintainers":[{"email":"team@photon.codes","name":"photon_dev"},{"email":"ryan@photon.codes","name":"ryanzhuuuu"},{"email":"me@qwerzl.me","name":"qwerzl"},{"email":"synapse-05.giddy@icloud.com","name":"lcandy"}],"readme":"<div align=\"center\">\n\n![Banner](./.github/asset/banner.png)\n   \n# notebooklm-kit\n\n> A TypeScript SDK for programmatic access to Google NotebookLM.\n\n</div>\n\n[![npm version](https://img.shields.io/npm/v/notebooklm-kit.svg)](https://www.npmjs.com/package/notebooklm-kit)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue.svg)](https://www.typescriptlang.org/)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n[![Discord](https://img.shields.io/badge/Discord-Join-5865F2.svg?logo=discord&logoColor=white)](https://discord.gg/bZd4CMd2H5)\n\n## Overview\n\nThe NotebookLM Kit provides a clean, service-based interface to all NotebookLM features. Perfect for building AI research assistants, study tools, content generators, and automated knowledge management systems.\n\n## Features\n\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>Method</th>\n<th>Example</th>\n</tr>\n</thead>\n<tbody>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td colspan=\"3\" style=\"padding-top: 20px; padding-bottom: 20px;\"><strong style=\"font-size: 1.2em;\"><a href=\"#notebooks\">Notebook Management</a></strong> <small><code>list()</code>, <code>create()</code>, <code>get()</code>, <code>update()</code>, <code>delete()</code>, <code>share()</code></small></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td>List Notebooks</td>\n<td><code>sdk.notebooks.list()</code></td>\n<td><a href=\"examples/notebook-list.ts\">notebook-list.ts</a></td>\n</tr>\n<tr>\n<td>Create Notebook</td>\n<td><code>sdk.notebooks.create()</code></td>\n<td><a href=\"examples/notebook-create.ts\">notebook-create.ts</a></td>\n</tr>\n<tr>\n<td>Get Notebook</td>\n<td><code>sdk.notebooks.get()</code></td>\n<td><a href=\"examples/notebook-get.ts\">notebook-get.ts</a></td>\n</tr>\n<tr>\n<td>Update Notebook</td>\n<td><code>sdk.notebooks.update()</code></td>\n<td><a href=\"examples/notebook-update.ts\">notebook-update.ts</a></td>\n</tr>\n<tr>\n<td>Delete Notebook</td>\n<td><code>sdk.notebooks.delete()</code></td>\n<td><a href=\"examples/notebook-delete.ts\">notebook-delete.ts</a></td>\n</tr>\n<tr>\n<td>Share Notebook <small>⚠️ Experimental</small></td>\n<td><code>sdk.notebooks.share()</code></td>\n<td><a href=\"examples/notebook-share.ts\">notebook-share.ts</a></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td colspan=\"3\" style=\"padding-top: 20px; padding-bottom: 20px;\"><strong style=\"font-size: 1.2em;\"><a href=\"#sources\">Source Management</a></strong> <small><code>list()</code>, <code>get()</code>, <code>add.url()</code>, <code>add.text()</code>, <code>add.youtube()</code>, <code>add.file()</code>, <code>add.drive()</code>, <code>add.batch()</code>, <code>add.web.searchAndWait()</code>, <code>update()</code>, <code>delete()</code>, <code>status()</code></small></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td>List Sources</td>\n<td><code>sdk.sources.list()</code></td>\n<td><a href=\"examples/source-list.ts\">source-list.ts</a></td>\n</tr>\n<tr>\n<td>Add URL Source</td>\n<td><code>sdk.sources.add.url()</code></td>\n<td><a href=\"examples/source-add-url.ts\">source-add-url.ts</a></td>\n</tr>\n<tr>\n<td>Add Text Source</td>\n<td><code>sdk.sources.add.text()</code></td>\n<td><a href=\"examples/source-add-text.ts\">source-add-text.ts</a></td>\n</tr>\n<tr>\n<td>Add YouTube Source</td>\n<td><code>sdk.sources.add.youtube()</code></td>\n<td><a href=\"examples/source-add-youtube.ts\">source-add-youtube.ts</a></td>\n</tr>\n<tr>\n<td>Add File Source</td>\n<td><code>sdk.sources.add.file()</code></td>\n<td><a href=\"examples/source-add-file.ts\">source-add-file.ts</a></td>\n</tr>\n<tr>\n<td>Add Drive Source <small>⚠️ Experimental</small></td>\n<td><code>sdk.sources.add.drive()</code></td>\n<td><a href=\"examples/source-add-drive.ts\">source-add-drive.ts</a></td>\n</tr>\n<tr>\n<td>Add Batch Sources</td>\n<td><code>sdk.sources.add.batch()</code></td>\n<td><a href=\"examples/source-add-batch.ts\">source-add-batch.ts</a></td>\n</tr>\n<tr>\n<td>Web Search Source</td>\n<td><code>sdk.sources.add.web.searchAndWait()</code></td>\n<td><a href=\"examples/source-web-search.ts\">source-web-search.ts</a></td>\n</tr>\n<tr>\n<td>Advanced Web Search</td>\n<td><code>sdk.sources.add.web.searchAndWait()</code></td>\n<td><a href=\"examples/source-web-search-advanced.ts\">source-web-search-advanced.ts</a></td>\n</tr>\n<tr>\n<td>Get Source</td>\n<td><code>sdk.sources.get()</code></td>\n<td><a href=\"examples/source-get.ts\">source-get.ts</a></td>\n</tr>\n<tr>\n<td>Update Source</td>\n<td><code>sdk.sources.update()</code></td>\n<td><a href=\"examples/source-update.ts\">source-update.ts</a></td>\n</tr>\n<tr>\n<td>Delete Source</td>\n<td><code>sdk.sources.delete()</code></td>\n<td><a href=\"examples/source-delete.ts\">source-delete.ts</a></td>\n</tr>\n<tr>\n<td>Check Source Status</td>\n<td><code>sdk.sources.status()</code></td>\n<td><a href=\"examples/source-status.ts\">source-status.ts</a></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td colspan=\"3\" style=\"padding-top: 20px; padding-bottom: 20px;\"><strong style=\"font-size: 1.2em;\"><a href=\"#artifacts\">Artifact Generation</a></strong> <small><code>create()</code>, <code>list()</code>, <code>get()</code>, <code>download()</code>, <code>rename()</code>, <code>delete()</code>, <code>share()</code></small></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td>Create Artifact</td>\n<td><code>sdk.artifacts.create()</code></td>\n<td><a href=\"examples/artifact-create.ts\">artifact-create.ts</a></td>\n</tr>\n<tr>\n<td>Create Artifact (Subservices)</td>\n<td><code>sdk.artifacts.{type}.create()</code></td>\n<td><a href=\"examples/artifact-create-subservices.ts\">artifact-create-subservices.ts</a></td>\n</tr>\n<tr>\n<td>List Artifacts</td>\n<td><code>sdk.artifacts.list()</code></td>\n<td><a href=\"examples/artifact-list.ts\">artifact-list.ts</a></td>\n</tr>\n<tr>\n<td>Get Artifact</td>\n<td><code>sdk.artifacts.get()</code></td>\n<td><a href=\"examples/artifact-get.ts\">artifact-get.ts</a></td>\n</tr>\n<tr>\n<td>Download Artifact</td>\n<td><code>sdk.artifacts.download()</code></td>\n<td><a href=\"examples/artifact-download.ts\">artifact-download.ts</a></td>\n</tr>\n<tr>\n<td>Download Video</td>\n<td><code>sdk.artifacts.download()</code></td>\n<td><a href=\"examples/artifact-video.ts\">artifact-video.ts</a></td>\n</tr>\n<tr>\n<td>Download Slides</td>\n<td><code>sdk.artifacts.download()</code></td>\n<td><a href=\"examples/slide-download-test.ts\">slide-download-test.ts</a></td>\n</tr>\n<tr>\n<td>Rename Artifact</td>\n<td><code>sdk.artifacts.rename()</code></td>\n<td><a href=\"examples/artifact-rename.ts\">artifact-rename.ts</a></td>\n</tr>\n<tr>\n<td>Delete Artifact</td>\n<td><code>sdk.artifacts.delete()</code></td>\n<td><a href=\"examples/artifact-delete.ts\">artifact-delete.ts</a></td>\n</tr>\n<tr>\n<td>Share Artifact</td>\n<td><code>sdk.artifacts.share()</code></td>\n<td><a href=\"examples/artifact-share.ts\">artifact-share.ts</a></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td colspan=\"3\" style=\"padding-top: 20px; padding-bottom: 20px;\"><strong style=\"font-size: 1.2em;\"><a href=\"#generation--chat\">Chat & Generation</a></strong> <small><code>chat()</code>, <code>chatStream()</code>, <code>setChatConfig()</code></small></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td>Chat</td>\n<td><code>sdk.generation.chat()</code></td>\n<td><a href=\"examples/chat-basic.ts\">chat-basic.ts</a></td>\n</tr>\n<tr>\n<td>Stream Chat</td>\n<td><code>sdk.generation.chatStream()</code></td>\n<td><a href=\"examples/chat-conversation.ts\">chat-conversation.ts</a></td>\n</tr>\n<tr>\n<td>Set Chat Config</td>\n<td><code>sdk.generation.setChatConfig()</code></td>\n<td><a href=\"examples/generation-set-chat-config.ts\">generation-set-chat-config.ts</a></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td colspan=\"3\" style=\"padding-top: 20px; padding-bottom: 20px;\"><strong style=\"font-size: 1.2em;\"><a href=\"#notes\">Notes Management</a></strong> <small><code>list()</code>, <code>create()</code>, <code>update()</code>, <code>delete()</code></small></td>\n</tr>\n<tr style=\"height: 30px;\"><td colspan=\"3\"></td></tr>\n<tr>\n<td>List Notes</td>\n<td><code>sdk.notes.list()</code></td>\n<td><a href=\"examples/note-list.ts\">note-list.ts</a></td>\n</tr>\n<tr>\n<td>Create Note</td>\n<td><code>sdk.notes.create()</code></td>\n<td><a href=\"examples/note-create.ts\">note-create.ts</a></td>\n</tr>\n<tr>\n<td>Update Note</td>\n<td><code>sdk.notes.update()</code></td>\n<td><a href=\"examples/note-update.ts\">note-update.ts</a></td>\n</tr>\n<tr>\n<td>Delete Note</td>\n<td><code>sdk.notes.delete()</code></td>\n<td><a href=\"examples/note-delete.ts\">note-delete.ts</a></td>\n</tr>\n</tbody>\n</table>\n\n## Installation\n\n```bash\nnpm install notebooklm-kit\n```\n\n**From source:**\n```bash\ngit clone https://github.com/photon-hq/notebooklm-kit.git && cd notebooklm-kit && npm run setup\n```\n\n**Requirements:** Node.js >=18.0.0\n\n## Version\n\nCurrent version: **2.2.0**\n\n## Available Scripts\n\nWhen working with the repository, you can use the following npm scripts:\n\n| Script | Description |\n|--------|-------------|\n| `npm install` | Install dependencies and automatically build (runs postinstall) |\n| `npm run setup` | Full setup: install dependencies, Playwright, and build |\n| `npm run build` | Compile TypeScript to JavaScript |\n| `npm run build:dev` | Build only (no reinstall) |\n| `npm run dev` | Watch mode (auto-rebuild on file changes) |\n| `npm run clean` | Remove compiled dist/ directory |\n\n<details>\n<summary><strong>Development</strong></summary>\n\n**First-time setup:**\n```bash\nnpm run setup\n```\n\n**Build only (no reinstall):**\n```bash\nnpm run build:dev\n```\n\n**Watch mode (auto-rebuild):**\n```bash\nnpm run dev\n```\n\n**Clean build:**\n```bash\nnpm run clean && npm run build\n```\n\n</details>\n\n## Quick Start\n\n### 1. Install the package\n\n```bash\nnpm install notebooklm-kit\n```\n\n### 2. Set up authentication\n\n**Auto (browser):** Create `.env` with `GOOGLE_EMAIL` and `GOOGLE_PASSWORD` (no 2FA required)\n\n**Manual:** Create `.env` with `NOTEBOOKLM_AUTH_TOKEN` and `NOTEBOOKLM_COOKIES` from browser DevTools (Network → Cookie header, Console → `window.WIZ_global_data.SNlM0e`)\n\n### 3. Use the SDK\n\n```typescript\nimport { NotebookLMClient } from 'notebooklm-kit';\nimport dotenv from 'dotenv';\n\n// Load .env file from project root (automatically detected)\ndotenv.config();\n\nasync function main() {\n  const sdk = new NotebookLMClient({\n    // Credentials are automatically loaded from .env file\n    // Priority: NOTEBOOKLM_AUTH_TOKEN/NOTEBOOKLM_COOKIES > GOOGLE_EMAIL/GOOGLE_PASSWORD\n  });\n\n  try {\n    await sdk.connect();\n\n    // List notebooks\n    const notebooks = await sdk.notebooks.list();\n    console.log(`Found ${notebooks.length} notebooks`);\n\n    // Create a notebook\n    const notebook = await sdk.notebooks.create({\n      title: 'My Research',\n      emoji: '📚',\n    });\n    console.log(`Created: ${notebook.title}`);\n\n  } catch (error) {\n    console.error('Error:', error);\n  } finally {\n    sdk.dispose();\n  }\n}\n\nmain();\n```\n\n**Note:** The SDK automatically loads credentials from environment variables. See [SDK Initialization](#sdk-initialization) for all configuration options.\n\n### Running Examples\n\nThe repository includes working examples in the [`examples/`](examples/) directory. To run them:\n\n1. **Set up your `.env` file** in the project root (see [Authentication](#2-set-up-authentication) above)\n2. **Run any example** using `tsx`:\n   ```bash\n   npx tsx examples/notebook-list.ts\n   npx tsx examples/chat-basic.ts\n   ```\n\n**Chat Example Usage:**\n```bash\n# Interactive mode (prompts for notebook and message)\nnpx tsx examples/chat-basic.ts\n\n# With notebook ID and message (streaming mode - default)\nnpx tsx examples/chat-basic.ts <notebook-id> \"What are the key findings?\"\n\n# Non-streaming mode (get complete response at once)\nnpx tsx examples/chat-basic.ts <notebook-id> \"What are the key findings?\" --no-stream\n```\n\n**Note:** The examples automatically detect and load the `.env` file from the project root, regardless of where you run them from.\n\n## Features\n\n### `sdk.notebooks` - Notebook Management\n\n| Feature | Description | Method | Example |\n|---------|-------------|--------|---------|\n| List Notebooks | List all your notebooks (recently viewed) | [`sdk.notebooks.list()`](#list-notebooks) | [notebook-list.ts](examples/notebook-list.ts) |\n| Get Notebook | Get full details of a specific notebook | [`sdk.notebooks.get(notebookId)`](#get-notebook) | [notebook-get.ts](examples/notebook-get.ts) |\n| Create Notebook | Create a new notebook (auto-generates title if empty) | [`sdk.notebooks.create(options)`](#create-notebook) | [notebook-create.ts](examples/notebook-create.ts) |\n| Update Notebook | Update notebook title or emoji | [`sdk.notebooks.update(notebookId, options)`](#update-notebook) | [notebook-update.ts](examples/notebook-update.ts) |\n| Delete Notebook | Delete one or more notebooks | [`sdk.notebooks.delete(notebookIds)`](#delete-notebook) | [notebook-delete.ts](examples/notebook-delete.ts) |\n| Share Notebook <small>⚠️ Experimental</small> | Share notebook with users or enable link sharing | [`sdk.notebooks.share(notebookId, options)`](#share-notebook) | [notebook-share.ts](examples/notebook-share.ts) |\n\n### `sdk.sources` - Source Management\n\n| Feature | Description | Method | Example |\n|---------|-------------|--------|---------|\n| List Sources | List all sources in a notebook | [`sdk.sources.list(notebookId)`](#list-sources) | [source-list.ts](examples/source-list.ts) |\n| Get Source | Get one or all sources | [`sdk.sources.get(notebookId, sourceId?)`](#get-source) | [source-get.ts](examples/source-get.ts) |\n| Add URL | Add a source from a web page URL | [`sdk.sources.add.url(notebookId, options)`](#add-url-source) | [source-add-url.ts](examples/source-add-url.ts) |\n| Add Text | Add a source from text content | [`sdk.sources.add.text(notebookId, options)`](#add-text-source) | [source-add-text.ts](examples/source-add-text.ts) |\n| Add File | Add a source from a file (PDF, image, etc.) | [`sdk.sources.add.file(notebookId, options)`](#add-file-source) | [source-add-file.ts](examples/source-add-file.ts) |\n| Add YouTube | Add a YouTube video as a source | [`sdk.sources.add.youtube(notebookId, options)`](#add-youtube-source) | [source-add-youtube.ts](examples/source-add-youtube.ts) |\n| Add Google Drive <small>⚠️ Experimental</small> | Add a Google Drive file as a source | [`sdk.sources.add.drive(notebookId, options)`](#add-google-drive-source) | [source-add-drive.ts](examples/source-add-drive.ts) |\n| Batch Add | Add multiple sources at once | [`sdk.sources.add.batch(notebookId, options)`](#batch-add-sources) | [source-add-batch.ts](examples/source-add-batch.ts) |\n| Web Search (Simple) | Search web and wait for results | [`sdk.sources.add.web.searchAndWait(notebookId, options)`](#web-search-simple) | [source-web-search.ts](examples/source-web-search.ts) |\n| Web Search (Advanced) | Multi-step web search workflow | [`sdk.sources.add.web.search()`](#web-search-advanced) → `getResults()` → `addDiscovered()` | [source-web-search-advanced.ts](examples/source-web-search-advanced.ts) |\n| Update Source | Update source metadata | [`sdk.sources.update(notebookId, sourceId, updates)`](#update-source) | [source-update.ts](examples/source-update.ts) |\n| Delete Source | Delete a source from a notebook | [`sdk.sources.delete(notebookId, sourceId)`](#delete-source) | [source-delete.ts](examples/source-delete.ts) |\n| Check Status | Check source processing status | [`sdk.sources.status(notebookId)`](#check-processing-status) | [source-status.ts](examples/source-status.ts) |\n\n### `sdk.artifacts` - Artifact Management\n\n| Feature | Description | Method | Example |\n|---------|-------------|--------|---------|\n| Create Artifact | Create study material (quiz, flashcards, mind map, etc.) | [`sdk.artifacts.create()`](#create-artifact) or `sdk.artifacts.{type}.create()` | [artifact-create.ts](examples/artifact-create.ts)<br>[artifact-create-subservices.ts](examples/artifact-create-subservices.ts) |\n| List Artifacts | List all artifacts in a notebook (with filtering) | [`sdk.artifacts.list()`](#list-artifacts) | [artifact-list.ts](examples/artifact-list.ts) |\n| Get Artifact | Get artifact details (auto-fetches content when ready) | [`sdk.artifacts.get()`](#get-artifact) | [artifact-get.ts](examples/artifact-get.ts) |\n| Download Artifact | Download artifact data to disk (quiz/flashcard JSON, audio file) | [`sdk.artifacts.download()`](#download-artifact) | [artifact-download.ts](examples/artifact-download.ts) |\n| Download Video | Download video artifact as MP4 file | [`sdk.artifacts.download()`](#download-artifact) | [artifact-video.ts](examples/artifact-video.ts) |\n| Download Slides | Download slide deck as PDF or PNG files | [`sdk.artifacts.download()`](#download-artifact) | [slide-download-test.ts](examples/slide-download-test.ts) |\n| Rename Artifact | Rename an artifact | [`sdk.artifacts.rename()`](#rename-artifact) | [artifact-rename.ts](examples/artifact-rename.ts) |\n| Delete Artifact | Delete an artifact | [`sdk.artifacts.delete()`](#delete-artifact) | [artifact-delete.ts](examples/artifact-delete.ts) |\n| Share Artifact | Share artifact/notebook with users or enable link sharing | [`sdk.artifacts.share()`](#share-artifact) | [artifact-share.ts](examples/artifact-share.ts) |\n\n### `sdk.generation` - Generation & Chat\n\n| Feature | Description | Method | Example |\n|---------|-------------|--------|---------|\n| Chat (Non-streaming) | Chat with notebook content - returns complete response | [`sdk.generation.chat(notebookId, prompt, options?)`](#chat) | [chat-basic.ts](examples/chat-basic.ts) |\n| Chat Stream | Chat with real-time streaming response chunks | [`sdk.generation.chatStream(notebookId, prompt, options?)`](#chat-stream) | [chat-basic.ts](examples/chat-basic.ts) |\n| Chat Conversation | Multi-turn conversations with history tracking | [`sdk.generation.chat(notebookId, prompt, { conversationHistory })`](#chat) | [chat-conversation.ts](examples/chat-conversation.ts) |\n| Set Chat Config | Configure chat (custom prompt, learning guide, response length) | [`sdk.generation.setChatConfig(notebookId, config)`](#set-chat-configuration) | [generation-set-chat-config.ts](examples/generation-set-chat-config.ts) |\n\n### `sdk.notes` - Notes Management\n\n| Feature | Description | Method | Example |\n|---------|-------------|--------|---------|\n| List Notes | List all notes in a notebook | [`sdk.notes.list(notebookId)`](#list-notes) | [note-list.ts](examples/note-list.ts) |\n| Create Note | Create a new note | [`sdk.notes.create(notebookId, options)`](#create-note) | [note-create.ts](examples/note-create.ts) |\n| Update Note | Update a note | [`sdk.notes.update(notebookId, noteId, options)`](#update-note) | [note-update.ts](examples/note-update.ts) |\n| Delete Note | Delete a note | [`sdk.notes.delete(notebookId, noteIds)`](#delete-note) | [note-delete.ts](examples/note-delete.ts) |\n\n## Core Concepts\n\n### SDK Initialization\n\n**Methods:** `sdk.connect()` | `sdk.dispose()`\n\n**Basic Usage:**\n```typescript\nimport { NotebookLMClient } from 'notebooklm-kit';\nimport dotenv from 'dotenv';\n\ndotenv.config(); // Load .env from project root\n\nconst sdk = new NotebookLMClient({\n  // Credentials loaded automatically from environment variables:\n  // NOTEBOOKLM_AUTH_TOKEN, NOTEBOOKLM_COOKIES\n  // or GOOGLE_EMAIL, GOOGLE_PASSWORD\n});\n\ntry {\n  await sdk.connect(); // Initialize SDK, authenticate, start auto-refresh\n  \n  // Now you can use sdk.notebooks, sdk.sources, etc.\n  const notebooks = await sdk.notebooks.list();\n  \n} finally {\n  sdk.dispose(); // Always cleanup\n}\n```\n\n**Explicit Credentials:**\n```typescript\nconst sdk = new NotebookLMClient({\n  authToken: process.env.NOTEBOOKLM_AUTH_TOKEN!,\n  cookies: process.env.NOTEBOOKLM_COOKIES!,\n  // or\n  auth: {\n    email: process.env.GOOGLE_EMAIL!,\n    password: process.env.GOOGLE_PASSWORD!,\n  },\n});\n\nawait sdk.connect();\n```\n\n**Enable Debug Mode:**\n\nDebug mode provides detailed logging for troubleshooting, API calls, authentication, and internal operations.\n\n**Option 1: Environment Variable (Recommended)**\n```bash\n# In your .env file\nNOTEBOOKLM_DEBUG=true\n```\n\n**Option 2: Config Option**\n```typescript\nconst sdk = new NotebookLMClient({\n  debug: true, // Enable debug logging\n  // ... other config\n});\n\nawait sdk.connect();\n```\n\n**Option 3: Development Mode**\n```bash\n# Automatically enables debug in development\nNODE_ENV=development\n```\n\n**What Debug Mode Logs:**\n- ✅ RPC call details (method names, arguments, responses)\n- ✅ Authentication flow (login steps, credential extraction)\n- ✅ Auto-refresh operations (token refresh attempts, timing)\n- ✅ Streaming responses (chunk processing, parsing)\n- ✅ Error details (full stack traces, API responses)\n- ✅ Response parsing (chunked data processing, validation)\n\n**Example Debug Output:**\n```\n[DEBUG] RPC Call: wXbhsf with args: [null, 1, null, [2]]\n[DEBUG] Response received: 200 OK\n[DEBUG] Parsing chunked response: 3 chunks found\n[DEBUG] Auto-refresh: Token expires in 5 minutes, refreshing now...\n[DEBUG] Authentication: Extracting credentials from browser...\n```\n\n**Disable Debug:**\n```typescript\n// Explicitly disable (overrides environment variable)\nconst sdk = new NotebookLMClient({\n  debug: false,\n});\n```\n\n```bash\n# Or in .env\nNOTEBOOKLM_DEBUG=false\n```\n\n<details>\n<summary><strong>Connection Flow</strong></summary>\n\n1. **Credentials Resolution** (in priority order):\n   - Provided in config (`authToken`/`cookies`)\n   - Environment variables (`NOTEBOOKLM_AUTH_TOKEN`/`NOTEBOOKLM_COOKIES`)\n   - Saved credentials (`credentials.json` in project root) - **reused automatically**\n   - Auto-login (if `auth.email`/`auth.password` provided) - **only if no saved credentials**\n   \n   **Note:** Set `FORCE_REAUTH=true` in `.env` to force re-authentication and ignore saved credentials\n\n2. **Initialization:**\n   - Creates RPC client with credentials\n   - Initializes all services (`notebooks`, `sources`, `artifacts`, etc.)\n   - Starts auto-refresh manager (if enabled)\n\n3. **Auto-Refresh:**\n   - Begins automatically after `connect()`\n   - Runs in background, doesn't block operations\n   - Updates credentials and cookies automatically\n\n</details>\n\n<details>\n<summary><strong>Cleanup</strong></summary>\n\n**Always call `dispose()` when done:**\n- Stops auto-refresh background timers\n- Prevents memory leaks\n- Resets client state\n- Required for graceful shutdown\n\n```typescript\ntry {\n  await sdk.connect();\n  // ... use SDK ...\n} finally {\n  await sdk.dispose(); // Always cleanup\n}\n```\n\n</details>\n\n### Authentication Overview\n\nAuthentication is handled automatically when you call `sdk.connect()`. Credentials are resolved in this priority order:\n\n1. Provided in config (`authToken`/`cookies`)\n2. Environment variables (`NOTEBOOKLM_AUTH_TOKEN`/`NOTEBOOKLM_COOKIES`)\n3. Saved credentials (`credentials.json` in project root)\n4. Auto-login (if `auth.email`/`auth.password` provided)\n\nSee the [Authentication](#authentication) section for detailed setup instructions and all configuration options.\n\n### Quota Limits\n\nReference: [Official Documentation](https://support.google.com/notebooklm/answer/16213268)\n\n**Plan Types:** `standard` (default) | `plus` | `pro` | `ultra`\n\n| Limit | Standard | Plus | Pro | Ultra |\n|-------|----------|------|-----|-------|\n| **Notebooks** | 100/user | 200/user | 500/user | 500/user |\n| **Sources/Notebook** | 50 | 100 | 300 | 600 |\n| **Words/Source** | 500,000 | 500,000 | 500,000 | 500,000 |\n| **File Size** | 200MB | 200MB | 200MB | 200MB |\n| **Chats/Day** | 50 | 200 | 500 | 5,000 |\n| **Audio/Video/Day** | 3 | 6 | 20 | 200 |\n| **Reports/Day** | 10 | 20 | 100 | 1,000 |\n| **Deep Research/Month** | 10 | 90 | 600 | 6,000 |\n| **Mind Maps** | Unlimited | Unlimited | Unlimited | Unlimited |\n\n<details>\n<summary><strong>Important Notes</strong></summary>\n\n- **Daily quotas** reset after 24 hours\n- **Monthly quotas** reset after 30 days\n- **Word/File Limits:** NotebookLM rejects sources >500k words or >200MB. Copy-protected PDFs cannot be imported.\n- **Server-Side Enforcement:** Data Tables, Infographics, Slides (limits vary)\n- **Client-Side Validation:** Optional (`enforceQuotas: true`), disabled by default\n- **Plan Selection:** Set during SDK initialization: `plan: 'pro'`\n\n</details>\n\n## Authentication\n\n### Auto-Login (Recommended)\n\n**Method:** Use `auth` config with email/password\n\n```typescript\nconst sdk = new NotebookLMClient({\n  auth: {\n    email: process.env.GOOGLE_EMAIL,\n    password: process.env.GOOGLE_PASSWORD,\n    headless: true, // default: true\n  },\n});\n\nawait sdk.connect(); // Logs in, extracts auth token, prompts for cookies, saves to credentials.json\n```\n\n<details>\n<summary><strong>Environment Variables (.env file)</strong></summary>\n\n**File Location:** Create `.env` in your **project root** directory (same directory as `package.json`).\n\n```bash\n# .env file location: /path/to/your-project/.env\n\n# Option 1: Auto-login with email/password (recommended - requires no 2FA)\nGOOGLE_EMAIL=\"your-email@gmail.com\"\nGOOGLE_PASSWORD=\"your-password\"\n\n# Option 2: Manual credentials (for production or when auto-login isn't available)\nNOTEBOOKLM_AUTH_TOKEN=\"ACi2F2NZSD7yrNvFMrCkP3vZJY1R:1766720233448\"\nNOTEBOOKLM_COOKIES=\"_ga=GA1.1.1949425436.1764104083; SID=g.a0005AiwX...; ...\"\n\n# Optional: Retry configuration\nNOTEBOOKLM_MAX_RETRIES=1          # Default: 1\nNOTEBOOKLM_RETRY_DELAY=1000       # Default: 1000ms\nNOTEBOOKLM_RETRY_MAX_DELAY=5000   # Default: 5000ms\n\n# Optional: Debug mode (enables detailed logging)\nNOTEBOOKLM_DEBUG=true             # Enable debug logging for troubleshooting\n\n# Optional: Force re-authentication (ignore saved credentials)\nFORCE_REAUTH=true\n```\n\n**Important:**\n- `.env` file must be in the **project root** (not in subdirectories)\n- Account must NOT have 2FA enabled (or use app-specific passwords)\n- The `.env` file is automatically ignored by git (see `.gitignore`)\n\n</details>\n\n### Manual Credentials\n\n**Method:** Provide `authToken` and `cookies` directly\n\n```typescript\nconst sdk = new NotebookLMClient({\n  authToken: process.env.NOTEBOOKLM_AUTH_TOKEN!,\n  cookies: process.env.NOTEBOOKLM_COOKIES!,\n  enforceQuotas: true, // optional\n  plan: 'standard', // optional: 'standard' | 'plus' | 'pro' | 'ultra'\n});\n\nawait sdk.connect();\n```\n\n<details>\n<summary><strong>Getting Credentials</strong></summary>\n\n1. **Auth Token**: Open https://notebooklm.google.com → DevTools (F12) → Console → Run: `window.WIZ_global_data.SNlM0e`\n2. **Cookies**: DevTools → Network tab → Any request → Headers → Copy Cookie value\n\n</details>\n\n### Saved Credentials\n\n**Location:** `credentials.json` in project root (e.g., `notebooklm-kit/credentials.json`)\n\nWhen using auto-login with email/password:\n1. Browser opens and authenticates\n2. Auth token is extracted automatically\n3. You're prompted to manually paste cookies\n4. Credentials are saved to `credentials.json` for future use\n\n**Subsequent runs:**\n- Saved credentials are automatically reused (no browser prompt)\n- Faster startup - no need to re-enter cookies\n- Credentials file is in project root for easy viewing/editing\n\n**To force re-authentication:**\n- Set `FORCE_REAUTH=true` in `.env`, or\n- Delete `credentials.json` file\n\n**Security Note:** `credentials.json` contains sensitive authentication data. It's automatically added to `.gitignore` to prevent accidental commits.\n\n### Auto-Refresh Configuration\n\n**Default:** Enabled with `'auto'` strategy (recommended)\n\n```typescriptimproved\n// Default: auto strategy (expiration-based + time-based fallback)\nconst sdk = new NotebookLMClient({\n  auth: { email: '...', password: '...' },\n  // autoRefresh: true (default)\n});\n\n// Time-based (simple, predictable)\nautoRefresh: { strategy: 'time', interval: 10 * 60 * 1000 }\n\n// Expiration-based (maximum efficiency)\nautoRefresh: { strategy: 'expiration', refreshAhead: 5 * 60 * 1000 }\n\n// Disable\nautoRefresh: false\n\n// Manual refresh\nawait sdk.refreshCredentials();\n```\n\n<details>\n<summary><strong>Auto-Refresh Details</strong></summary>\n\n- Credentials updated automatically after refresh\n- Cookies kept in sync\n- Runs in background, doesn't block operations\n- See [Auto-Refresh Strategies](#auto-refresh-strategies) in Core Concepts\n\n</details>\n\n### Quota Management\n\n**Method:** `sdk.getUsage()` | `sdk.getRemaining()` | `sdk.getQuotaManager()`\n\n```typescript\nconst sdk = new NotebookLMClient({\n  auth: { email: '...', password: '...' },\n  enforceQuotas: true, // Enable client-side validation (disabled by default)\n  plan: 'pro', // Set plan for accurate limits\n});\n\nawait sdk.connect();\n\n// Check usage\nconst usage = sdk.getUsage();\nconst remaining = sdk.getRemaining('chats');\nconst limits = sdk.getQuotaManager().getLimits();\n```\n\n<details>\n<summary><strong>Quota Notes</strong></summary>\n\n- **Disabled by default** - enable with `enforceQuotas: true`\n- Throws `RateLimitError` if limit exceeded (when enabled)\n- Server-side enforcement always active (even if client-side disabled)\n- See [Quota Limits](#quota-limits) table in Core Concepts\n\n</details>\n\n## Notebooks\n\nExamples: [notebook-list.ts](examples/notebook-list.ts) | [notebook-get.ts](examples/notebook-get.ts) | [notebook-create.ts](examples/notebook-create.ts) | [notebook-update.ts](examples/notebook-update.ts) | [notebook-delete.ts](examples/notebook-delete.ts) | [notebook-share.ts](examples/notebook-share.ts)\n\n### List Notebooks\n\n**Method:** `sdk.notebooks.list()`\n\n**Example:** [notebook-list.ts](examples/notebook-list.ts)\n\n**Returns:** `Promise<Notebook[]>`\n\n**Description:**\nLists all your notebooks (recently viewed). Returns a lightweight array of notebooks with essential information for display/selection.\n\n**Return Fields:**\n- `projectId: string` - Unique notebook ID (required for other operations)\n- `title: string` - Notebook title\n- `emoji: string` - Visual identifier\n- `sourceCount: number` - Number of sources in the notebook\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Automatically filters out system notebooks (e.g., \"OpenStax's Biology\")\n- Returns only notebooks you've recently viewed\n- Does not include full notebook details (use `get()` for that)\n- Does not include sources array (use `sources` service for source operations)\n- Does not include sharing info (use `get()` for sharing details)\n\n</details>\n\n**Usage:**\n```typescript\nconst notebooks = await sdk.notebooks.list()\nconsole.log(`Found ${notebooks.length} notebooks`)\nnotebooks.forEach(nb => {\n  console.log(`${nb.emoji} ${nb.title} (${nb.sourceCount} sources)`)\n})\n```\n\n---\n\n### Get Notebook\n\n**Method:** `sdk.notebooks.get(notebookId)`\n\n**Example:** [notebook-get.ts](examples/notebook-get.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n\n**Returns:** `Promise<Notebook>`\n\n**Description:**\nRetrieves full details of a specific notebook, including analytics and sharing information. Makes parallel RPC calls to get complete notebook data.\n\n**Return Fields:**\n- `projectId: string` - Unique notebook ID\n- `title: string` - Notebook title\n- `emoji: string` - Visual identifier\n- `sourceCount?: number` - Number of sources (analytics)\n- `lastAccessed?: string` - Last accessed timestamp (ISO format, analytics)\n- `sharing?: SharingSettings` - Sharing configuration:\n  - `isShared: boolean` - Whether notebook is shared\n  - `shareUrl?: string` - Share URL if shared\n  - `shareId?: string` - Share ID\n  - `publicAccess?: boolean` - Whether public access is enabled\n  - `allowedUsers?: string[]` - Array of user emails with access\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Validates notebook ID format before making RPC calls\n- Calls both `RPC_GET_PROJECT` and `RPC_GET_SHARING_DETAILS` in parallel for efficiency\n- Sharing data is optional - won't fail if unavailable\n- Does not include sources array (use `sources` service for source operations)\n- `lastAccessed` is extracted from notebook metadata if available\n\n</details>\n\n**Usage:**\n```typescript\nconst notebook = await sdk.notebooks.get('notebook-id')\nconsole.log(`Title: ${notebook.title}`)\nconsole.log(`Sources: ${notebook.sourceCount || 0}`)\nconsole.log(`Last accessed: ${notebook.lastAccessed || 'Never'}`)\nif (notebook.sharing?.isShared) {\n  console.log(`Share URL: ${notebook.sharing.shareUrl}`)\n}\n```\n\n---\n\n### Create Notebook\n\n**Method:** `sdk.notebooks.create(options)`\n\n**Example:** [notebook-create.ts](examples/notebook-create.ts)\n\n**Parameters:**\n- `options: CreateNotebookOptions`\n  - `title: string` - Notebook title (optional, auto-generated if empty)\n  - `emoji?: string` - Notebook emoji (optional)\n\n**Returns:** `Promise<Notebook>`\n\n**Description:**\nCreates a new notebook. Automatically generates a title if not provided. Validates title length before creation.\n\n**Return Fields:**\n- `projectId: string` - Unique notebook ID (use this for subsequent operations)\n- `title: string` - Notebook title (as provided or auto-generated)\n- `emoji: string` - Default emoji\n\n**Auto-Generated Title Format:**\nIf `title` is empty or not provided, generates: `\"Untitled Notebook {current date}\"`\nExample: `\"Untitled Notebook 12/30/2024\"`\n\n<details>\n<summary><strong>Validation</strong></summary>\n\n- Title maximum length: 100 characters\n- Throws `APIError` if title exceeds limit\n- Empty title is allowed (will be auto-generated)\n\n</details>\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Quota is checked before creation (if quota manager is enabled)\n- Usage is recorded after successful creation\n- Returns immediately with notebook ID - no waiting required\n- Does not include `sourceCount`, `lastAccessed`, or `sharing` (not available for new notebooks)\n\n</details>\n\n**Usage:**\n```typescript\n// With title\nconst notebook = await sdk.notebooks.create({\n  title: 'My Research Project',\n})\n\n// With title and emoji\nconst notebook = await sdk.notebooks.create({\n  title: 'My Research Project',\n  emoji: '📚',\n})\n\n// Auto-generated title\nconst untitled = await sdk.notebooks.create({})\n```\n\n---\n\n### Update Notebook\n\n**Method:** `sdk.notebooks.update(notebookId, options)`\n\n**Example:** [notebook-update.ts](examples/notebook-update.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required, automatically trimmed)\n- `options: UpdateNotebookOptions`\n  - `title?: string` - New title (optional)\n  - `emoji?: string` - New emoji (optional)\n  - `metadata?: Record<string, any>` - Other metadata updates (optional)\n\n**Returns:** `Promise<Notebook>` (same as `get()` - full notebook details)\n\n**Description:**\nUpdates notebook title or emoji. Returns full notebook details after update (same structure as `get()`). Supports updating emoji only, title only, or both together.\n\n<details>\n<summary><strong>Validation</strong></summary>\n\n- At least one field (`title` or `emoji`) must be provided\n- Title maximum length: 100 characters\n- Notebook ID is automatically trimmed (removes trailing spaces)\n- Returns error if notebook doesn't exist\n\n</details>\n\n**Return Fields:**\nSame as `get()` - includes `projectId`, `title`, `emoji`, `sourceCount`, `lastAccessed`, `sharing`\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Notebook ID is trimmed automatically to prevent issues with trailing spaces\n- Only provided fields are updated (partial updates supported)\n- Returns full notebook object after update (not just updated fields)\n- Does not validate notebook existence first (for performance) - returns error if not found\n\n</details>\n\n**Usage:**\n```typescript\n// Update title only\nconst updated = await sdk.notebooks.update('notebook-id', {\n  title: 'Updated Title',\n})\n\n// Update emoji only\nconst updated = await sdk.notebooks.update('notebook-id', {\n  emoji: '🔥',\n})\n\n// Update both title and emoji\nconst updated = await sdk.notebooks.update('notebook-id', {\n  title: 'New Title',\n  emoji: '⭐',\n})\n\n// Update all fields\nconst updated = await sdk.notebooks.update('notebook-id', {\n  title: 'New Title',\n  emoji: '🎯',\n})\n```\n\n---\n\n### Delete Notebook\n\n**Method:** `sdk.notebooks.delete(notebookIds, options?)`\n\n**Example:** [notebook-delete.ts](examples/notebook-delete.ts)\n\n**Parameters:**\n- `notebookIds: string | string[]` - Single notebook ID or array of IDs (required)\n- `options?: DeleteNotebookOptions` - Optional deletion options:\n  - `mode?: 'parallel' | 'sequential'` - Execution mode (default: 'parallel')\n\n**Returns:** `Promise<DeleteNotebookResult>`\n\n**Description:**\nDeletes one or more notebooks. For multiple notebooks, deletions are performed individually (either in parallel or sequentially) since Google's API does not support true batch deletion. Returns confirmation with deleted IDs and count.\n\n**Return Fields:**\n- `deleted: string[]` - Array of successfully deleted notebook IDs\n- `count: number` - Number of notebooks successfully deleted\n- `failed?: string[]` - Array of notebook IDs that failed to delete (only present if some failed)\n- `failedCount?: number` - Number of notebooks that failed to delete\n\n<details>\n<summary><strong>Validation</strong></summary>\n\n- All provided IDs are validated before deletion\n- Throws `APIError` if any ID is invalid\n- Supports both single ID and array of IDs\n\n</details>\n\n<details>\n<summary><strong>Deletion Modes</strong></summary>\n\n- **Parallel (default):** All notebooks are deleted simultaneously using `Promise.all()`. Faster but may hit rate limits.\n- **Sequential:** Notebooks are deleted one at a time. Slower but more reliable and avoids rate limit issues.\n\n</details>\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- No confirmation required - deletion is immediate\n- Google's API does not support batch deletion in a single call\n- Multiple notebooks are deleted individually, one per API call\n- Parallel mode is default but sequential mode is recommended for large batches to avoid rate limits\n- Failed deletions are tracked separately - if some succeed and some fail, you'll get both `deleted` and `failed` arrays\n- Throws error only if ALL deletions fail (partial failures return result with `failed` array)\n\n</details>\n\n**Usage:**\n```typescript\n// Delete single notebook\nconst result = await sdk.notebooks.delete('notebook-id')\nconsole.log(`Deleted ${result.count} notebook: ${result.deleted[0]}`)\n\n// Delete multiple notebooks (parallel - default)\nconst result = await sdk.notebooks.delete(['id-1', 'id-2', 'id-3'])\nconsole.log(`Deleted ${result.count} notebooks: ${result.deleted.join(', ')}`)\n\n// Delete multiple notebooks (sequential - recommended for large batches)\nconst result = await sdk.notebooks.delete(['id-1', 'id-2', 'id-3'], { mode: 'sequential' })\nconsole.log(`Deleted ${result.count} notebooks: ${result.deleted.join(', ')}`)\nif (result.failed && result.failed.length > 0) {\n  console.log(`Failed to delete: ${result.failed.join(', ')}`)\n}\n```\n\n---\n\n### Share Notebook\n\n> **⚠️ Experimental:** This feature is experimental and may have limitations or breaking changes in future versions.\n\n**Method:** `sdk.notebooks.share(notebookId, options)`\n\n**Example:** [notebook-share.ts](examples/notebook-share.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required, automatically trimmed)\n- `options: ShareNotebookOptions`\n  - `users?: Array<{email: string, role: 2|3|4}>` - Users to share with (optional)\n  - `notify?: boolean` - Notify users (default: true, only used when users are provided)\n  - `accessType?: 1|2` - Access type: 1=anyone with link, 2=restricted (optional, default: 2)\n\n**Returns:** `Promise<ShareNotebookResult>`\n\n**Description:**\nShares notebook with users or enables link sharing. Supports multiple users with different roles. Automatically fetches updated sharing state after operation.\n\n**User Roles:**\n\n| Role | Value | Description |\n|------|-------|-------------|\n| Editor | `2` | Can edit notebook content |\n| Viewer | `3` | Can view notebook only |\n| Remove | `4` | Remove user from shared list |\n\n**Access Types:**\n\n| Access Type | Value | Description |\n|-------------|-------|-------------|\n| Anyone with link | `1` | Public access via share link |\n| Restricted | `2` | Only specified users can access |\n\n**Return Fields:**\n- `shareUrl: string` - Share URL (always present, even if not shared)\n- `success: boolean` - Whether the share operation succeeded\n- `notebookId: string` - The notebook ID that was shared\n- `accessType: 1|2` - Access type: 1=anyone with link, 2=restricted\n- `isShared: boolean` - Whether the notebook is shared (true if shared with users or link enabled)\n- `users?: Array<{email: string, role: 2|3}>` - Users with access (only present if users were shared)\n\n**Notify Behavior:**\n- `notify` is only used when `users` are provided\n- Default: `true` (users are notified when permissions change)\n- Set to `false` to share silently\n- Not used when only changing link access (no user changes)\n\n<details>\n<summary><strong>Validation</strong></summary>\n\n- Email addresses are validated using regex before sharing\n- Throws `APIError` if any email is invalid\n- Supports multiple users in a single call\n\n</details>\n\n<details>\n<summary><strong>Notes</strong></summary>\n- Notebook ID is automatically trimmed\n- Makes prerequisite `JFMDGd` call before sharing (to initialize sharing state)\n- After successful share, makes another `JFMDGd` call to fetch updated state\n- `shareUrl` is always returned (constructed from notebook ID if not explicitly shared)\n- Supports sharing with multiple users in a single operation\n- Can combine user sharing with link access in one call\n\n</details>\n\n**Usage:**\n```typescript\n// Share with users (restricted access, notify enabled by default)\nconst result = await sdk.notebooks.share('notebook-id', {\n  users: [\n    { email: 'user1@example.com', role: 2 }, // editor\n    { email: 'user2@example.com', role: 3 }, // viewer\n  ],\n  notify: true,\n  accessType: 2, // restricted\n})\n\n// Share with users (silent, no notification)\nconst result = await sdk.notebooks.share('notebook-id', {\n  users: [\n    { email: 'user@example.com', role: 2 },\n  ],\n  notify: false,\n  accessType: 2,\n})\n\n// Enable link sharing (anyone with link)\nconst result = await sdk.notebooks.share('notebook-id', {\n  accessType: 1, // 1=anyone with link, 2=restricted\n})\n\n// Remove user (role: 4)\nconst result = await sdk.notebooks.share('notebook-id', {\n  users: [\n    { email: 'user@example.com', role: 4 }, // remove\n  ],\n  accessType: 2,\n})\n```\n\n<details>\n<summary><b>Sources</b> - Add & manage sources</summary>\n\n### Methods\n\n#### `addFromURL(notebookId: string, options: AddURLSourceOptions)` → `Promise<string>`\nAdd a source from a URL (web page, YouTube, etc.).\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n- `options.url: string` - URL to add\n\n**Returns:**\n- `string` - Source ID\n\n**Example:**\n```typescript\nconst sourceId = await sdk.sources.addFromURL('notebook-id', {\n  url: 'https://example.com/article',\n})\n```\n\n---\n\n#### `addFromText(notebookId: string, options: AddTextSourceOptions)` → `Promise<string | AddSourceResult>`\nAdd a source from text content.\n\n**Auto-Chunking:** Large texts (>500k words) are automatically split into chunks and uploaded in parallel.\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n- `options.title: string` - Source title\n- `options.content: string` - Text content\n\n**Returns:**\n- `string` - Source ID (if not chunked)\n- `AddSourceResult` - Chunk metadata (if auto-chunked)\n\n**Example:**\n```typescript\n// Small text (returns string)\nconst sourceId = await sdk.sources.addFromText('notebook-id', {\n  title: 'Research Notes',\n  content: 'Your text content here...',\n})\n\n// Large text (auto-chunked)\nconst result = await sdk.sources.addFromText('notebook-id', {\n  title: 'Large Document',\n  content: veryLongText, // > 500k words\n})\nif (typeof result === 'string') {\n  console.log(`Source ID: ${result}`)\n} else {\n  console.log(`Uploaded ${result.chunks?.length || 0} chunks`)\n}\n```\n\n---\n\n#### `addFromFile(notebookId: string, options: AddFileSourceOptions)` → `Promise<string | AddSourceResult>`\nAdd a source from a file (PDF, image, etc.).\n\n**Auto-Chunking:** Large files (>200MB or >500k words) are automatically split into chunks and uploaded in parallel.\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n- `options.content: Buffer | string` - File content as Buffer or base64 string\n- `options.fileName: string` - File name\n- `options.mimeType?: string` - MIME type (e.g., 'application/pdf')\n\n**Returns:**\n- `string` - Source ID (if not chunked)\n- `AddSourceResult` - Chunk metadata (if auto-chunked)\n\n**Example:**\n```typescript\nimport { readFile } from 'fs/promises'\n\n// Small file (returns string)\nconst buffer = await readFile('./document.pdf')\nconst sourceId = await sdk.sources.addFromFile('notebook-id', {\n  content: buffer,\n  fileName: 'document.pdf',\n  mimeType: 'application/pdf',\n})\n\n// Large file (auto-chunked)\nconst largeBuffer = await readFile('./large-document.pdf')\nconst result = await sdk.sources.addFromFile('notebook-id', {\n  content: largeBuffer, // > 200MB or > 500k words\n  fileName: 'large-document.pdf',\n})\nif (typeof result === 'string') {\n  console.log(`Source ID: ${result}`)\n} else {\n  console.log(`Uploaded ${result.chunks?.length || 0} chunks`)\n}\n```\n\n---\n\n#### `addYouTube(notebookId: string, options: AddYouTubeSourceOptions)` → `Promise<string>`\nAdd a YouTube video as a source.\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n- `options.urlOrId: string` - YouTube URL or video ID\n\n**Returns:**\n- `string` - Source ID\n\n**Example:**\n```typescript\nconst sourceId = await sdk.sources.addYouTube('notebook-id', {\n  urlOrId: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',\n})\n```\n\n---\n\n#### `searchWebAndWait(notebookId: string, options: SearchWebOptions)` → `Promise<SearchWebResult>`\nSearch the web or Google Drive and wait for results.\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n- `options.query: string` - Search query\n- `options.sourceType: SearchSourceType` - WEB or GOOGLE_DRIVE\n- `options.mode: ResearchMode` - STANDARD or DEEP\n\n**Returns:**\n- `SearchWebResult` - Object with:\n  - `sessionId: string` - Session ID for adding sources\n  - `sources: Array<{sourceId: string, title: string, ...}>` - Found sources\n\n**Example:**\n```typescript\nimport { SearchSourceType, ResearchMode } from 'notebooklm-kit'\n\nconst result = await sdk.sources.searchWebAndWait('notebook-id', {\n  query: 'machine learning trends 2024',\n  sourceType: SearchSourceType.WEB,\n  mode: ResearchMode.STANDARD,\n})\n\n// Add selected sources\nconst sourceIds = await sdk.sources.addDiscovered('notebook-id', {\n  sessionId: result.sessionId,\n  sourceIds: result.sources.slice(0, 5).map(s => s.sourceId),\n})\n```\n\n---\n\n#### `pollProcessing(notebookId: string)` → `Promise<SourceProcessingStatus>`\nCheck source processing status.\n\n**Parameters:**\n- `notebookId: string` - The notebook ID\n\n**Returns:**\n- `SourceProcessingStatus` - Object with:\n  - `readyCount: number` - Number of ready sources\n  - `totalCount: number` - Total sources\n  - `processingCount: number` - Sources being processed\n  - `failedCount: number` - Failed sources\n  - `allReady: boolean` - Whether all sources are ready\n\n**Example:**\n```typescript\nconst status = await sdk.sources.pollProcessing('notebook-id')\nconsole.log(`Ready: ${status.readyCount}/${status.totalCount}`)\n```\n\n</details>\n\n## Sources\n\nExamples: [source-list.ts](examples/source-list.ts) | [source-get.ts](examples/source-get.ts) | [source-add-url.ts](examples/source-add-url.ts) | [source-add-text.ts](examples/source-add-text.ts) | [source-add-file.ts](examples/source-add-file.ts) | [source-add-youtube.ts](examples/source-add-youtube.ts) | [source-add-drive.ts](examples/source-add-drive.ts) | [source-add-batch.ts](examples/source-add-batch.ts) | [source-web-search.ts](examples/source-web-search.ts) | [source-web-search-advanced.ts](examples/source-web-search-advanced.ts) | [source-update.ts](examples/source-update.ts) | [source-delete.ts](examples/source-delete.ts) | [source-status.ts](examples/source-status.ts)\n\n### List Sources\n\n**Method:** `sdk.sources.list(notebookId)`\n\n**Example:** [source-list.ts](examples/source-list.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n\n**Returns:** `Promise<Source[]>`\n\n**Description:**\nRetrieves a list of all sources (URLs, text, files, YouTube videos, Google Drive files, etc.) associated with a notebook. Sources are extracted from the notebook response efficiently without requiring a separate RPC call.\n\n**Return Fields:**\n- `sourceId: string` - Unique identifier for the source\n- `title?: string` - Source title/name\n- `type?: SourceType` - Source type (URL, TEXT, PDF, YOUTUBE_VIDEO, GOOGLE_DRIVE, IMAGE, etc.)\n- `url?: string` - Source URL (for URL/YouTube sources)\n- `createdAt?: string` - Creation timestamp (ISO format)\n- `updatedAt?: string` - Last modified timestamp (ISO format)\n- `status?: SourceStatus` - Processing status (`PROCESSING`, `READY`, `FAILED`)\n- `metadata?: Record<string, any>` - Additional metadata (file size, MIME type, etc.)\n\n**Source Types:**\n- `URL` - Web page URL\n- `TEXT` - Text content\n- `PDF` - PDF file\n- `YOUTUBE_VIDEO` - YouTube video\n- `GOOGLE_DRIVE` - Google Drive file\n- `IMAGE` - Image file\n- `VIDEO_FILE` - Video file upload\n- `PDF_FROM_DRIVE` - PDF from Google Drive\n- `TEXT_NOTE` - Text note\n- `MIND_MAP_NOTE` - Mind map note\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Sources are extracted from the notebook response (same RPC as `notebooks.get()`)\n- Processing status is inferred from source metadata\n- Returns empty array if notebook has no sources\n- File size and MIME type are included in metadata when available\n\n</details>\n\n**Usage:**\n```typescript\n// List all sources\nconst sources = await sdk.sources.list('notebook-id')\nconsole.log(`Found ${sources.length} sources`)\n\n// Filter by type\nconst pdfs = sources.filter(s => s.type === SourceType.PDF)\nconst urls = sources.filter(s => s.type === SourceType.URL)\n\n// Check processing status\nconst ready = sources.filter(s => s.status === SourceStatus.READY)\nconst processing = sources.filter(s => s.status === SourceStatus.PROCESSING)\n```\n\n---\n\n### Get Source\n\n**Method:** `sdk.sources.get(notebookId, sourceId?)`\n\n**Example:** [source-get.ts](examples/source-get.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `sourceId?: string` - Optional source ID to get a single source\n\n**Returns:** `Promise<Source | Source[]>` - Single source if `sourceId` provided, array of all sources if omitted\n\n**Description:**\nGet one or all sources from a notebook. If `sourceId` is provided, returns a single source. If omitted, returns all sources (same as `list()`).\n\n**Return Fields:**\nSame as `list()` - see [List Sources](#list-sources) for field descriptions.\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Returns array if `sourceId` is omitted (same as `list()`)\n- Returns single source object if `sourceId` is provided\n- Throws error if source not found when `sourceId` is provided\n- Efficiently reuses notebook data (no separate RPC call)\n\n</details>\n\n**Usage:**\n```typescript\n// Get all sources\nconst allSources = await sdk.sources.get('notebook-id')\n\n// Get specific source\nconst source = await sdk.sources.get('notebook-id', 'source-id')\nconsole.log(source.title)\n```\n\n---\n\n### Add URL Source\n\n**Method:** `sdk.sources.add.url(notebookId, options)`\n\n**Example:** [source-add-url.ts](examples/source-add-url.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddSourceFromURLOptions`\n  - `url: string` - URL to add (required)\n  - `title?: string` - Optional custom title\n\n**Returns:** `Promise<string>` - Source ID\n\n**Description:**\nAdds a web page URL as a source. Returns immediately after source is queued. Use `status()` to check if source is ready.\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Returns immediately after source is queued (does not wait for processing)\n- Quota is checked before adding\n- Use `status()` to check if source is ready\n- URL must be a valid HTTP/HTTPS URL\n\n</details>\n\n**Usage:**\n```typescript\nconst sourceId = await sdk.sources.add.url('notebook-id', {\n  url: 'https://ai.google.dev/',\n  title: 'Google AI Developer',\n})\n\n// Check if ready\nconst status = await sdk.sources.status('notebook-id')\nif (!status.processing.includes(sourceId)) {\n  console.log('Source is ready!')\n}\n```\n\n---\n\n### Add Text Source\n\n**Method:** `sdk.sources.add.text(notebookId, options)`\n\n**Example:** [source-add-text.ts](examples/source-add-text.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddSourceFromTextOptions`\n  - `content: string` - Text content (required)\n  - `title: string` - Source title (required)\n\n**Returns:** `Promise<string | AddSourceResult>` - Source ID (string) if not chunked, or `AddSourceResult` if auto-chunked\n\n**Description:**\nAdds text content as a source. Useful for adding notes, research summaries, or any text-based content.\n\n**Auto-Chunking:**\n- If text exceeds 500,000 words, it's automatically split into chunks and uploaded in parallel\n- Each chunk is uploaded as a separate source (counts toward your source limit)\n- Returns `AddSourceResult` with chunk count and source IDs when chunked\n- Small texts (≤500k words) return a simple string (backward compatible)\n\n<details>\n<summary><strong>Auto-Chunking Details</strong></summary>\n\n- **Limit:** 500,000 words per source\n- **Behavior:** Large texts are automatically split into optimal chunks\n- **Upload:** All chunks are uploaded in parallel for faster processing\n- **Result:** Returns chunk metadata including number of chunks and all source IDs\n\n</details>\n\n**Usage:**\n```typescript\n// Small text (returns string - backward compatible)\nconst sourceId = await sdk.sources.add.text('notebook-id', {\n  title: 'Research Notes',\n  content: 'Key findings from research...',\n})\n\n// Large text (auto-chunked - returns AddSourceResult)\nconst result = await sdk.sources.add.text('notebook-id', {\n  title: 'Large Document',\n  content: veryLongText, // > 500k words\n})\nif (typeof result === 'string') {\n  console.log(`Source ID: ${result}`)\n} else {\n  console.log(`Uploaded ${result.chunks?.length || 0} chunks`)\n  console.log(`Source IDs: ${result.allSourceIds?.join(', ')}`)\n}\n```\n\n---\n\n### Add File Source\n\n**Method:** `sdk.sources.add.file(notebookId, options)`\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddSourceFromFileOptions`\n  - `content: Buffer | string` - File content as Buffer or base64 string (required)\n  - `fileName: string` - File name (required)\n  - `mimeType?: string` - MIME type (optional, auto-detected if not provided)\n\n**Returns:** `Promise<string | AddSourceResult>` - Source ID (string) if not chunked, or `AddSourceResult` if auto-chunked\n\n**Description:**\nAdds a file (PDF, image, video, etc.) as a source. Supports files as Buffer or base64 string.\n\n**Auto-Chunking:**\n- Files exceeding 200MB or containing more than 500,000 words are automatically split into chunks\n- Text-based files (txt, md, csv, json, etc.): Chunked by word count (500k words per chunk)\n- Binary files: Chunked by size (200MB per chunk)\n- All chunks are uploaded in parallel for faster processing\n- Each chunk counts as a separate source toward your source limit\n- Small files return a simple string (backward compatible)\n\n<details>\n<summary><strong>Auto-Chunking Details</strong></summary>\n\n- **Size Limit:** 200MB per source\n- **Word Limit:** 500,000 words per source\n- **Text Files:** Automatically extracted and chunked by word count\n- **Binary Files:** Chunked by file size\n- **PDFs:** Chunked by size (text extraction requires a PDF library)\n- **Result:** Returns chunk metadata including number of chunks and all source IDs\n\n</details>\n\n**Supported File Types:**\n- PDF files\n- Image files (PNG, JPG, etc.)\n- Video files\n- Text files (txt, md, csv, json, etc.)\n- Other document types\n\n**Usage:**\n```typescript\nimport fs from 'fs'\n\n// Small file (returns string - backward compatible)\nconst fileBuffer = fs.readFileSync('document.pdf')\nconst sourceId = await sdk.sources.add.file('notebook-id', {\n  content: fileBuffer,\n  fileName: 'document.pdf',\n  mimeType: 'application/pdf',\n})\n\n// Large file (auto-chunked - returns AddSourceResult)\nconst largeFileBuffer = fs.readFileSync('large-document.pdf')\nconst result = await sdk.sources.add.file('notebook-id', {\n  content: largeFileBuffer, // > 200MB or > 500k words\n  fileName: 'large-document.pdf',\n})\nif (typeof result === 'string') {\n  console.log(`Source ID: ${result}`)\n} else {\n  console.log(`Uploaded ${result.chunks?.length || 0} chunks`)\n  console.log(`Source IDs: ${result.allSourceIds?.join(', ')}`)\n}\n```\n\n---\n\n### Add YouTube Source\n\n**Method:** `sdk.sources.add.youtube(notebookId, options)`\n\n**Example:** [source-add-youtube.ts](examples/source-add-youtube.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddYouTubeSourceOptions`\n  - `urlOrId: string` - YouTube URL or video ID (required)\n  - `title?: string` - Optional custom title\n\n**Returns:** `Promise<string>` - Source ID\n\n**Description:**\nAdds a YouTube video as a source. Accepts either full YouTube URL or just the video ID.\n\n**Usage:**\n```typescript\n// From YouTube URL\nconst sourceId = await sdk.sources.add.youtube('notebook-id', {\n  urlOrId: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',\n})\n\n// From video ID\nconst sourceId = await sdk.sources.add.youtube('notebook-id', {\n  urlOrId: 'dQw4w9WgXcQ',\n})\n```\n\n---\n\n### Add Google Drive Source\n\n> **⚠️ Experimental:** This feature is experimental and may have limitations or breaking changes in future versions.\n\n**Method:** `sdk.sources.add.drive(notebookId, options)`\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddGoogleDriveSourceOptions`\n  - `fileId: string` - Google Drive file ID (required)\n  - `title?: string` - Optional custom title\n  - `mimeType?: string` - MIME type (optional, inferred if not provided)\n\n**Returns:** `Promise<string>` - Source ID\n\n**Description:**\nAdds a Google Drive file as a source. Requires the file ID from Google Drive.\n\n<details>\n<summary><strong>Deprecated</strong></summary>\n\nThis method is deprecated. Use `add.batch()` with `type: 'gdrive'` instead.\n\n</details>\n\n**Usage:**\n```typescript\nconst sourceId = await sdk.sources.add.drive('notebook-id', {\n  fileId: '1a2b3c4d5e6f7g8h9i0j',\n  mimeType: 'application/vnd.google-apps.document',\n  title: 'My Document',\n})\n```\n\n---\n\n### Batch Add Sources\n\n**Method:** `sdk.sources.add.batch(notebookId, options)`\n\n**Example:** [source-add-batch.ts](examples/source-add-batch.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: BatchAddSourcesOptions`\n  - `sources: Array<...>` - Array of source inputs (required)\n  - `waitForProcessing?: boolean` - Whether to wait for all sources to be processed (default: false)\n  - `timeout?: number` - Timeout in ms if `waitForProcessing` is true (default: 300000 = 5 minutes)\n  - `pollInterval?: number` - Poll interval in ms (default: 2000 = 2 seconds)\n  - `onProgress?: (ready: number, total: number) => void` - Progress callback\n\n**Returns:** `Promise<string[]>` - Array of source IDs\n\n**Description:**\nAdds multiple sources at once. Supports mixed source types (URLs, text, files, YouTube, Google Drive) in a single call.\n\n**Source Types:**\n- `{ type: 'url', url: string, title?: string }` - URL source\n- `{ type: 'text', title: string, content: string }` - Text source\n- `{ type: 'file', content: Buffer | string, fileName: string, mimeType?: string }` - File source\n- `{ type: 'youtube', urlOrId: string, title?: string }` - YouTube source\n- `{ type: 'gdrive', fileId: string, title?: string, mimeType?: string }` - Google Drive source <small>⚠️ Experimental</small>\n\n**Usage:**\n```typescript\nconst sourceIds = await sdk.sources.add.batch('notebook-id', {\n  sources: [\n    { type: 'url', url: 'https://example.com', title: 'Example' },\n    { type: 'text', title: 'Notes', content: 'Content here...' },\n    { type: 'youtube', urlOrId: 'dQw4w9WgXcQ' },\n  ],\n  waitForProcessing: true, // Optional: wait for all to be ready\n  timeout: 300000, // 5 minutes\n  onProgress: (ready, total) => {\n    console.log(`Progress: ${ready}/${total}`)\n  },\n})\n```\n\n---\n\n### Web Search (Simple)\n\n**Method:** `sdk.sources.add.web.searchAndWait(notebookId, options)`\n\n**Example:** [source-web-search.ts](examples/source-web-search.ts)\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: SearchWebAndWaitOptions`\n  - `query: string` - Search query (required)\n  - `sourceType?: SearchSourceType` - Source type: `WEB` (default) or `GOOGLE_DRIVE`\n  - `mode?: ResearchMode` - Research mode: `FAST` (default) or `DEEP` (web only)\n  - `timeout?: number` - Max wait time in ms (default: 60000 = 60 seconds)\n  - `pollInterval?: number` - Poll interval in ms (default: 2000 = 2 seconds)\n  - `onProgress?: (status) => void` - Progress callback\n\n**Returns:** `Promise<WebSearchResult>` - Results with `sessionId`, `web` sources, and `drive` sources\n\n**Description:**\n**RECOMMENDED FOR SIMPLE WORKFLOWS** - One call that searches and waits for results automatically. Returns all discovered sources once available (or timeout). Perfect for automated workflows where you don't need to see intermediate steps.\n\n**Research Modes:**\n- `ResearchMode.FAST` - Quick search (~10-30 seconds, default)\n- `ResearchMode.DEEP` - Comprehensive research (~60-120 seconds, web only)\n\n**Source Types:**\n- `SearchSourceType.WEB` - Search web (default)\n- `SearchSourceType.GOOGLE_DRIVE` - Search Google Drive (FAST mode only) <small>⚠️ Experimental</small>\n\n**Return Fields:**\n- `sessionId: string` - Required for adding sources (use with `addDiscovered()`)\n- `web: DiscoveredWebSource[]` - Discovered web sources\n- `drive: DiscoveredDriveSource[]` - Discovered Google Drive sources <small>⚠️ Experimental</small>\n\n<details>\n<summary><strong>Notes</strong></summary>\n\n- Automatically polls for results until available or timeout\n- Returns results once count stabilizes (assumes search complete)\n- Progress callback shows result count as search progresses\n- Use returned `sessionId` with `addDiscovered()` to add selected sources\n\n</details>\n\n**Usage:**\n```typescript\nimport { ResearchMode, SearchSourceType } from 'notebooklm-kit'\n\n// Simple search and wait\nconst result = await sdk.sources.add.web.searchAndWait('notebook-id', {\n  query: 'machine learning research papers 2024',\n  mode: ResearchMode.DEEP, // Comprehensive search\n  sourceType: SearchSourceType.WEB,\n  timeout: 120000, // Wait up to 2 minutes\n  onProgress: (status) => {\n    console.log(`Found ${status.resultCount} results so far...`)\n  },\n})\n\nconsole.log(`Found ${result.web.length} web sources`)\nconsole.log(`Session ID: ${result.sessionId}`)\n\n// Add selected sources\nconst addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {\n  sessionId: result.sessionId,\n  webSources: result.web.slice(0, 5), // Top 5\n})\n```\n\n---\n\n### Web Search (Advanced)\n\n**Method:** `sdk.sources.add.web.search(notebookId, options)` → `sdk.sources.add.web.getResults(notebookId, sessionId)` → `sdk.sources.add.web.addDiscovered(notebookId, options)`\n\n**Example:** [source-web-search-advanced.ts](examples/source-web-search-advanced.ts)\n\n**Description:**\n**MULTI-STEP WORKFLOW** - For cases where you want to see results and make decisions at each step. Returns intermediate results so you can validate, filter, or select before adding sources.\n\n**Workflow Steps:**\n1. **`search()`** - Start search, returns `sessionId` immediately\n2. **`getResults(sessionId)`** - Get discovered sources (can call multiple times to poll)\n3. **`addDiscovered(sessionId, selectedSources)`** - Add your selected sources\n\n**Step 1: Start Search**\n\n**Method:** `sdk.sources.add.web.search(notebookId, options)`\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: SearchWebSourcesOptions`\n  - `query: string` - Search query (required)\n  - `sourceType?: SearchSourceType` - `WEB` (default) or `GOOGLE_DRIVE`\n  - `mode?: ResearchMode` - `FAST` (default) or `DEEP` (web only)\n\n**Returns:** `Promise<string>` - Session ID (required for steps 2 and 3)\n\n**Step 2: Get Results**\n\n**Method:** `sdk.sources.add.web.getResults(notebookId, sessionId?)`\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `sessionId?: string` - Session ID from step 1 (optional - if omitted, returns all results)\n\n**Returns:** `Promise<{ web: DiscoveredWebSource[], drive: DiscoveredDriveSource[] }>`\n\n**Step 3: Add Discovered Sources**\n\n**Method:** `sdk.sources.add.web.addDiscovered(notebookId, options)`\n\n**Parameters:**\n- `notebookId: string` - The notebook ID (required)\n- `options: AddDiscoveredSourcesOptions`\n  - `sessionId: string` - Session ID from step 1 (required)\n  - `webSources?: DiscoveredWebSource[]` - Web sources to add\n  - `driveSources?: DiscoveredDriveSource[]` - Drive sources to add\n\n**Returns:** `Promise<string[]>` - Array of added source IDs\n\n**Usage:**\n```typescript\n// Step 1: Start search\nconst sessionId = await sdk.sources.add.web.search('notebook-id', {\n  query: 'quantum computing',\n  mode: ResearchMode.FAST,\n})\n\n// Step 2: Poll for results (you control when/how often)\nlet results\ndo {\n  await new Promise(r => setTimeout(r, 2000)) // Wait 2 seconds\n  results = await sdk.sources.add.web.getResults('notebook-id', sessionId)\n  console.log(`Found ${results.web.length} sources...`)\n} while (results.web.length === 0)\n\n// Step 3: Filter and add selected sources\nconst relevant = results.web.filter(s => s.url.includes('arxiv.org'))\nconst addedIds = await sdk.sources.add.web.addDiscovered('notebook-id', {\n  sessionId,","readmeFilename":"README.md"}