Conversation
Add --prompt CLI option to run an LLM agent block ad-hoc: - `deepnote run --prompt "..."` creates a standalone in-memory notebook - `deepnote run file.deepnote --prompt "..."` appends an LLM block Thread database integration metadata through to the LLM agent system prompt so it can discover and query configured integrations. Update docs and skill references. Made-with: Cursor
📝 WalkthroughWalkthroughThis PR introduces Agent Block support: new agent block schemas and MCP server config, CLI --prompt flow (including in-memory prompt runs), runtime integration for agent execution and streaming events, a new agent-handler implementing OpenAI Agents tooling and MCP lifecycle, execution-engine changes to run agent blocks and surface agent events, new utilities for extracting output text, tests, docs, examples (demo agent notebook), and various small infra updates (.gitignore, cspell, deps, types). Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as CLI (run)
participant RunCmd as Run Command Handler
participant ExecEngine as Execution Engine
participant AgentHdlr as Agent Handler
participant OpenAI as OpenAI Agents API
participant MCPServer as MCP Server
participant Kernel as Kernel Client
User->>CLI: deepnote run --prompt "analyze data"
CLI->>RunCmd: createRunAction(path, options)
RunCmd->>RunCmd: setupProject(path, prompt)
alt no path, prompt provided
RunCmd->>RunCmd: createPromptOnlyFile(prompt)
else path provided
RunCmd->>RunCmd: appendAgentBlock(prompt)
end
RunCmd->>ExecEngine: runDeepnoteProject(file, options)
loop per executable block
ExecEngine->>ExecEngine: identify block type
alt agent block
ExecEngine->>AgentHdlr: executeAgentBlock(block, context)
AgentHdlr->>AgentHdlr: resolveEnvVars(), buildSystemPrompt()
AgentHdlr->>MCPServer: connect MCP servers
AgentHdlr->>OpenAI: start agent with tools
OpenAI-->>AgentHdlr: stream text_delta / tool_call
alt tool_call add_code_block
AgentHdlr->>Kernel: run code
Kernel-->>AgentHdlr: outputs
AgentHdlr->>ExecEngine: onAgentEvent(tool_output)
else tool_call add_markdown_block
AgentHdlr->>AgentHdlr: insert markdown block
end
AgentHdlr-->>ExecEngine: AgentBlockResult (finalOutput, addedBlockIds, blockOutputs)
AgentHdlr->>MCPServer: cleanup
else regular block
ExecEngine->>Kernel: execute code block
Kernel-->>ExecEngine: outputs
end
end
ExecEngine-->>RunCmd: aggregated results
RunCmd->>CLI: render outputs (streaming + final)
CLI-->>User: display notebook and results
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Codecov Report❌ Patch coverage is ❌ Your patch status has failed because the patch coverage (55.30%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #335 +/- ##
==========================================
- Coverage 83.49% 82.61% -0.89%
==========================================
Files 124 126 +2
Lines 7375 7582 +207
Branches 2046 2048 +2
==========================================
+ Hits 6158 6264 +106
- Misses 1217 1317 +100
- Partials 0 1 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.gitignore:
- Around line 1-38: The .gitignore contains duplicated entries (e.g., repeated
lines like "!packages/**/*.png", ".DS_Store", ".env", ".npmrc", ".pnpm-store",
".qlty/*", "*.png", "*.tsbuildinfo", "coverage", "dist", "examples/report.md",
snapshot patterns, and "node_modules")—remove the duplicate lines so each ignore
pattern appears only once; edit the .gitignore to deduplicate all identical
lines (keeping a single canonical occurrence of each pattern) and ensure negated
patterns (like !packages/**/*.png) remain correctly placed and not duplicated.
In `@cspell.json`:
- Line 73: Move the "agentic" entry in cspell.json to the proper alphabetical
position: remove the duplicate at its current location and insert "agentic"
before "Instantiator" (and therefore before "asname") so the list remains
alphabetized; update the array to contain "agentic" only in its correct spot.
In `@packages/blocks/src/deepnote-file/llm-block-schema.test.ts`:
- Around line 31-45: The test object passed to deepnoteFileSchema.safeParse is
missing required top-level fields "environment" and "execution", causing
spurious failures; update the object in llm-block-schema.test.ts (the one
assigned to result) to include minimal valid "environment" and "execution"
entries that match deepnoteFileSchema (e.g., provide the minimal shapes expected
by the schema such as an environment object and an execution object/array or
their empty/default forms) so the safeParse covers the intended fields without
failing for missing top-level properties.
In `@packages/cli/package.json`:
- Around line 57-58: Update the package.json dependency entry for "marked" from
"^15.0.12" to "^17.0.3" (the dependency key is "marked") and then run your
package manager (npm install or yarn install) to refresh lockfile and
node_modules; verify no breaking changes by running the test suite or a quick
markdown rendering check after installing.
In `@packages/cli/src/commands/run.ts`:
- Around line 998-1004: The call to marked.use(markedTerminal()) is mutating
global marked state on every block completion inside the llmStreamed handling;
move the initialization so it runs once (e.g., at module top-level or inside
runDeepnoteProject before streaming begins) instead of inside the callback that
checks llmStreamed and llmTextBuffer. Locate the marked.use(markedTerminal())
invocation near the llmStreamed/llmTextBuffer block and remove it from the
callback; add a single initialization call for marked.use(markedTerminal()) near
the module initialization or at the start of runDeepnoteProject so that
marked.parse(llmTextBuffer) can be used without repeatedly mutating state.
Ensure markedTerminal is still imported where used.
In `@packages/mcp/src/tools/writing.ts`:
- Around line 413-417: The code computes a boolean `candidate` by checking
`spec.type` against a hardcoded list; this duplicates logic from
`@deepnote/runtime-core`. Replace the inline list and startsWith checks with the
shared exported set `executableBlockTypeSet` (and any helper like
`isExecutableBlockType` if available) so `candidate` is determined by checking
membership in `executableBlockTypeSet` and preserving the `input-` prefix rule
if that isn't covered by the shared symbol; update references to `spec.type`,
`candidate`, and import `executableBlockTypeSet` from `@deepnote/runtime-core` to
ensure consistency.
In `@packages/runtime-core/src/execution-engine.ts`:
- Around line 183-188: The code currently records blockIndex when building
allExecutableBlocks (the push that sets blockIndex) which can become stale after
earlier LLM insertions; instead stop storing a fixed blockIndex and resolve the
current index at execution/insertion time by using
notebook.blocks.indexOf(block) (or compute the target insertion position
relative to the current notebook.blocks array) right before inserting
LLM-generated blocks; update any usage of llmBlockIndex (the variable passed at
line ~224 and the similar code around 213-225) to recompute the index from the
live notebook.blocks array so generated blocks are inserted in the correct,
up-to-date location.
In `@packages/runtime-core/src/llm-handler.ts`:
- Around line 167-169: The current loop that calls mcpServers.forEach(await
server.connect()) runs outside the try/finally so if one connect() throws,
previously connected servers are never cleaned up; fix by either moving the
connect loop into the surrounding try block before the finally cleanup, or
explicitly track successfully connected servers (e.g., push to a
connectedServers array as each server.connect() resolves) and in the
catch/finally iterate that array to call the servers' cleanup method (the
existing close/disconnect code used later). Apply the same pattern to the other
connect sequence referenced around the 303-347 range.
- Around line 192-203: The DeepnoteBlock construction uses an unsafe "as any"
cast; replace it by explicitly typing the code block using Extract to preserve
the discriminated union (e.g., use Extract<DeepnoteBlock, { type: 'code' }> when
creating newBlock) and do the same for the markdown variant at the other site
(use Extract<DeepnoteBlock, { type: 'markdown' }>); update the object literals
(the one assigned to newBlock and the one around line ~281) so their shape
matches the Extract type (fields like id, blockGroup, sortingKey from
generateSortingKey(insertIndex), type, content, metadata, executionCount,
outputs for code blocks; the appropriate fields for markdown) and remove the "as
any" casts.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6b35bb24-264b-479a-b040-9ae7befa5a2f
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (31)
.gitignorecspell.jsonexamples/README.mdexamples/demo-llm-block.deepnoteexamples/snapshots/agentic-analysis-demo_demo-llm-agentic_latest.snapshot.deepnotepackages/blocks/src/blocks/executable-blocks.tspackages/blocks/src/blocks/llm-blocks.test.tspackages/blocks/src/blocks/llm-blocks.tspackages/blocks/src/deepnote-file/deepnote-file-schema.tspackages/blocks/src/deepnote-file/llm-block-schema.test.tspackages/blocks/src/index.tspackages/blocks/src/python-code.test.tspackages/blocks/src/python-code.tspackages/cli/README.mdpackages/cli/package.jsonpackages/cli/src/cli.tspackages/cli/src/commands/run.test.tspackages/cli/src/commands/run.tspackages/cli/src/marked-terminal.d.tspackages/mcp/src/tools/writing.tspackages/runtime-core/README.mdpackages/runtime-core/package.jsonpackages/runtime-core/src/execution-engine.test.tspackages/runtime-core/src/execution-engine.tspackages/runtime-core/src/index.tspackages/runtime-core/src/llm-handler.test.tspackages/runtime-core/src/llm-handler.tsskills/deepnote/references/blocks-llm.mdskills/deepnote/references/cli-run.mdskills/deepnote/references/schema.tstest-fixtures/llm-block.deepnote
| const result = deepnoteFileSchema.safeParse({ | ||
| version: '1.0.0', | ||
| metadata: { createdAt: '2026-01-01T00:00:00Z' }, | ||
| project: { | ||
| id: 'test', | ||
| name: 'Test', | ||
| settings: { | ||
| mcpServers: [ | ||
| { name: 'test-server', command: 'python', args: ['-m', 'server'] }, | ||
| { name: 'another', command: 'npx', env: { KEY: 'val' } }, | ||
| ], | ||
| }, | ||
| notebooks: [], | ||
| }, | ||
| }) |
There was a problem hiding this comment.
safeParse test object is missing required top-level schema fields.
Line 31 builds a Deepnote file object without environment and execution, but deepnoteFileSchema requires them. This can make the test fail for the wrong reason.
Proposed fix
it('validates project-level mcpServers in settings', () => {
- const result = deepnoteFileSchema.safeParse({
- version: '1.0.0',
- metadata: { createdAt: '2026-01-01T00:00:00Z' },
- project: {
- id: 'test',
- name: 'Test',
- settings: {
- mcpServers: [
- { name: 'test-server', command: 'python', args: ['-m', 'server'] },
- { name: 'another', command: 'npx', env: { KEY: 'val' } },
- ],
- },
- notebooks: [],
- },
- })
+ const fixturePath = path.join(__dirname, '../../../../test-fixtures/llm-block.deepnote')
+ const base = deserializeDeepnoteFile(fs.readFileSync(fixturePath, 'utf-8'))
+ const result = deepnoteFileSchema.safeParse({
+ ...base,
+ project: {
+ ...base.project,
+ settings: {
+ ...base.project.settings,
+ mcpServers: [
+ { name: 'test-server', command: 'python', args: ['-m', 'server'] },
+ { name: 'another', command: 'npx', env: { KEY: 'val' } },
+ ],
+ },
+ },
+ })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/deepnote-file/llm-block-schema.test.ts` around lines 31 -
45, The test object passed to deepnoteFileSchema.safeParse is missing required
top-level fields "environment" and "execution", causing spurious failures;
update the object in llm-block-schema.test.ts (the one assigned to result) to
include minimal valid "environment" and "execution" entries that match
deepnoteFileSchema (e.g., provide the minimal shapes expected by the schema such
as an environment object and an execution object/array or their empty/default
forms) so the safeParse covers the intended fields without failing for missing
top-level properties.
There was a problem hiding this comment.
Verified: the test passes as-is because environment and execution are both optional in deepnoteFileSchema (both use .optional()). No change needed.
There was a problem hiding this comment.
@jamesbhobbs, thanks for verifying — my mistake. Good to know the test is valid as-is.
✏️ Learnings added
Learnt from: jamesbhobbs
Repo: deepnote/deepnote PR: 335
File: packages/blocks/src/deepnote-file/llm-block-schema.test.ts:31-45
Timestamp: 2026-03-04T15:29:44.758Z
Learning: In `packages/blocks/src/deepnote-file/deepnote-file-schema.ts`, both `environment` and `execution` are optional fields (`.optional()`) in `deepnoteFileSchema`. Tests using `deepnoteFileSchema.safeParse` do not need to supply these fields.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to packages/blocks/src/**/*.{ts,tsx} : When making changes to `.deepnote` file format, schema, or block types, update `skills/deepnote/references/blocks-*.md` and `skills/deepnote/references/schema.ts`
Learnt from: jamesbhobbs
Repo: deepnote/deepnote PR: 18401
File: scripts/check-chromatic-enabled/pr-template.test.ts:1-6
Timestamp: 2025-09-14T18:25:57.101Z
Learning: In the deepnote/deepnote repository, colocated tests use Node's built-in test runner (node:test, node:assert/strict) rather than Jest.
Learnt from: saltenasl
Repo: deepnote/deepnote PR: 201
File: packages/blocks/src/deserialize-file/deepnote-file-schema.ts:7-8
Timestamp: 2026-01-08T12:52:57.703Z
Learning: In packages/blocks/src/deserialize-file/deepnote-file-schema.ts, the emptyContent helper uses z.preprocess(() => '', z.literal('').optional()) intentionally. The .optional() allows the TypeScript type to be optional even though the preprocessor always normalizes to ''. This pattern is preferred for maintaining type-level optionality while ensuring runtime normalization.
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to **/*.test.{ts,tsx} : Follow existing test patterns in the codebase (see packages/blocks/src/blocks/*.test.ts)
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to **/*.test.{ts,tsx} : Group related tests with `describe()` blocks in test files
Learnt from: dinohamzic
Repo: deepnote/deepnote PR: 227
File: packages/cli/src/commands/validate.ts:52-58
Timestamp: 2026-01-22T14:17:22.234Z
Learning: In the deepnote CLI validate command (packages/cli/src/commands/validate.ts), different JSON output shapes are intentional: `{ valid, path, issues }` for validation errors and `{ success, error }` for file resolution/runtime errors, to distinguish error classes.
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to **/*.test.{ts,tsx} : For functions that generate code, test the exact output format
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to **/*.test.{ts,tsx} : Test edge cases, error handling, and special characters in test files
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to **/*.test.{ts,tsx} : Import test utilities from vitest: `import { describe, expect, it } from 'vitest'`
Learnt from: jankuca
Repo: deepnote/deepnote PR: 18415
File: libs/shared/src/deepnote-ai/ai-chat-schema.test.ts:1-5
Timestamp: 2025-09-16T13:05:15.049Z
Learning: In the deepnote project, while assert import is restricted in libs/shared/.eslintrc.json, jankuca confirms that assert is allowed in test files according to project conventions, overriding the ESLint restriction.
Addresses review comment by @coderabbitai. Co-Authored-By: Claude <[email protected]>
- Deduplicate cspell.json "agentic" entry and fix alphabetical order - Move marked.use(markedTerminal()) to module level in run.ts - Resolve LLM block index at execution time in execution-engine.ts - Move MCP server connect() inside try/finally in llm-handler.ts - Replace `as any` casts with Extract types in llm-handler.ts - Update marked to ^17.0.3 Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 5
♻️ Duplicate comments (1)
.gitignore (1)
1-36:⚠️ Potential issue | 🟡 MinorDeduplicate
.gitignoreentries.Most patterns are repeated twice. Keep one canonical copy of each entry; this looks like a merge artifact.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.gitignore around lines 1 - 36, The .gitignore contains duplicated lines (e.g., repeated entries like !packages/**/*.png, .DS_Store, .env, .npmrc, .pnpm-store, .qlty/* directories, *.png, *.tsbuildinfo, coverage, dist, examples/report.md, examples/snapshots/*_T*.snapshot.deepnote, node_modules) likely from a merge; clean up the file by removing duplicate entries so each pattern appears only once, leaving a single canonical copy of each ignore rule and preserving the existing semantics/order.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/cli/package.json`:
- Around line 57-58: The dependency "marked" currently set to a v17 range
conflicts with "marked-terminal" v7.3.0's peer constraint (requires <16); update
the "marked" entry in package.json to a compatible v15 (or any <16) release to
satisfy the peer dependency, then reinstall/update the lockfile (npm/yarn/pnpm)
and run the test/build to ensure nothing breaks; target the "marked" and
"marked-terminal" dependency entries when making the change.
In `@packages/cli/src/commands/run.ts`:
- Around line 274-279: The current branch silently no-ops when
file.project.notebooks is empty; update the logic in run.ts so when path &&
options.prompt and file.project.notebooks.length === 0 you either emit a clear
warning (e.g., processLogger.warn or console.warn) and then create a new
notebook and push the created LLM block into it, or throw a descriptive error;
use the existing createLlmBlock(...) to build the block and ensure you add a new
notebook object with a blocks array (containing the created block) to
file.project.notebooks instead of silently doing nothing (refer to lastNotebook,
file.project.notebooks, and createLlmBlock in your change).
In `@packages/runtime-core/src/llm-handler.ts`:
- Around line 38-40: The generateSortingKey function returns
lexicographic-unfriendly keys like "a1"/"a10" causing misordering; update
generateSortingKey(index: number) to emit fixed-width numeric keys by
zero-padding the index to a constant width (e.g. pad to 6 or more digits) so
lexicographic sorting preserves numeric order—adjust the padding width to
support your maximum expected inserts.
- Around line 52-84: The notebook serialization in llm-handler.ts currently
inlines every block and full outputs (loop over notebook.blocks,
collectedOutputs.get(block.id), pushing block.content and outputs into lines)
which can exceed token limits; fix by implementing bounded serialization: limit
the number of blocks processed (e.g., stop after MAX_BLOCKS), truncate
block.content to MAX_CHARS_PER_BLOCK with an explicit "[truncated]" marker, cap
output size by only including the first N output entries and first M
lines/characters per output (replace large HTML/image outputs with placeholders
like "[HTML output truncated]" or "[Image omitted]"), and ensure error/metadata
fields are stringified safely; add or use constants (MAX_BLOCKS,
MAX_CHARS_PER_BLOCK, MAX_OUTPUT_ENTRIES, MAX_OUTPUT_CHARS) and apply these
limits inside the for (const block of notebook.blocks) and outputs loop to
prevent unbounded growth.
- Around line 129-142: The code currently throws if OPENAI_API_KEY is missing
even when OPENAI_BASE_URL is set; change the guard to only require apiKey when
OPENAI_BASE_URL is not provided (i.e., if (!apiKey &&
!process.env.OPENAI_BASE_URL) throw ...). When creating the OpenAI client (new
OpenAI), pass a safe dummy apiKey when baseURL is present because the SDK
expects an apiKey parameter (e.g., apiKey || '' or a short placeholder) so the
constructor receives a value but real auth uses the custom base URL; keep
baseURL set from process.env.OPENAI_BASE_URL and leave setTracingDisabled(true)
unchanged.
---
Duplicate comments:
In @.gitignore:
- Around line 1-36: The .gitignore contains duplicated lines (e.g., repeated
entries like !packages/**/*.png, .DS_Store, .env, .npmrc, .pnpm-store, .qlty/*
directories, *.png, *.tsbuildinfo, coverage, dist, examples/report.md,
examples/snapshots/*_T*.snapshot.deepnote, node_modules) likely from a merge;
clean up the file by removing duplicate entries so each pattern appears only
once, leaving a single canonical copy of each ignore rule and preserving the
existing semantics/order.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1073000f-1602-4d9a-adfa-febc424235a5
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (6)
.gitignorecspell.jsonpackages/cli/package.jsonpackages/cli/src/commands/run.tspackages/runtime-core/src/execution-engine.tspackages/runtime-core/src/llm-handler.ts
| for (const block of notebook.blocks) { | ||
| lines.push(`## Block [${block.type}] (id: ${block.id.slice(0, 8)})`) | ||
|
|
||
| if (block.content) { | ||
| lines.push('```') | ||
| lines.push(block.content) | ||
| lines.push('```') | ||
| } | ||
|
|
||
| const outputs = collectedOutputs.get(block.id) | ||
| if (outputs && outputs.outputs.length > 0) { | ||
| lines.push('### Output:') | ||
| for (const output of outputs.outputs) { | ||
| const out = output as Record<string, unknown> | ||
| if (out.output_type === 'stream' && typeof out.text === 'string') { | ||
| lines.push(out.text) | ||
| } else if (out.output_type === 'execute_result' || out.output_type === 'display_data') { | ||
| const data = out.data as Record<string, unknown> | undefined | ||
| if (data?.['text/plain']) { | ||
| lines.push(String(data['text/plain'])) | ||
| } else if (data?.['text/html']) { | ||
| lines.push('[HTML output]') | ||
| } else if (data?.['image/png'] || data?.['image/jpeg']) { | ||
| lines.push('[Image output]') | ||
| } | ||
| } else if (out.output_type === 'error') { | ||
| lines.push(`Error: ${out.ename}: ${out.evalue}`) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| lines.push('') | ||
| } |
There was a problem hiding this comment.
Notebook context serialization is unbounded.
This currently appends every block and full outputs. Large notebooks can blow token limits and degrade/abort runs.
Proposed fix
export function serializeNotebookContext(
file: DeepnoteFile,
notebookIndex: number,
collectedOutputs: Map<string, { outputs: unknown[]; executionCount: number | null }>
): string {
+ const MAX_BLOCKS = 50
+ const MAX_OUTPUT_CHARS = 4000
const notebook = file.project.notebooks[notebookIndex]
if (!notebook) return 'Empty notebook.'
@@
- for (const block of notebook.blocks) {
+ for (const block of notebook.blocks.slice(-MAX_BLOCKS)) {
@@
- lines.push(out.text)
+ lines.push(out.text.slice(0, MAX_OUTPUT_CHARS))
@@
- lines.push(String(data['text/plain']))
+ lines.push(String(data['text/plain']).slice(0, MAX_OUTPUT_CHARS))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const block of notebook.blocks) { | |
| lines.push(`## Block [${block.type}] (id: ${block.id.slice(0, 8)})`) | |
| if (block.content) { | |
| lines.push('```') | |
| lines.push(block.content) | |
| lines.push('```') | |
| } | |
| const outputs = collectedOutputs.get(block.id) | |
| if (outputs && outputs.outputs.length > 0) { | |
| lines.push('### Output:') | |
| for (const output of outputs.outputs) { | |
| const out = output as Record<string, unknown> | |
| if (out.output_type === 'stream' && typeof out.text === 'string') { | |
| lines.push(out.text) | |
| } else if (out.output_type === 'execute_result' || out.output_type === 'display_data') { | |
| const data = out.data as Record<string, unknown> | undefined | |
| if (data?.['text/plain']) { | |
| lines.push(String(data['text/plain'])) | |
| } else if (data?.['text/html']) { | |
| lines.push('[HTML output]') | |
| } else if (data?.['image/png'] || data?.['image/jpeg']) { | |
| lines.push('[Image output]') | |
| } | |
| } else if (out.output_type === 'error') { | |
| lines.push(`Error: ${out.ename}: ${out.evalue}`) | |
| } | |
| } | |
| } | |
| lines.push('') | |
| } | |
| for (const block of notebook.blocks.slice(-MAX_BLOCKS)) { | |
| lines.push(`## Block [${block.type}] (id: ${block.id.slice(0, 8)})`) | |
| if (block.content) { | |
| lines.push(' |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/runtime-core/src/llm-handler.ts` around lines 52 - 84, The notebook
serialization in llm-handler.ts currently inlines every block and full outputs
(loop over notebook.blocks, collectedOutputs.get(block.id), pushing
block.content and outputs into lines) which can exceed token limits; fix by
implementing bounded serialization: limit the number of blocks processed (e.g.,
stop after MAX_BLOCKS), truncate block.content to MAX_CHARS_PER_BLOCK with an
explicit "[truncated]" marker, cap output size by only including the first N
output entries and first M lines/characters per output (replace large HTML/image
outputs with placeholders like "[HTML output truncated]" or "[Image omitted]"),
and ensure error/metadata fields are stringified safely; add or use constants
(MAX_BLOCKS, MAX_CHARS_PER_BLOCK, MAX_OUTPUT_ENTRIES, MAX_OUTPUT_CHARS) and
apply these limits inside the for (const block of notebook.blocks) and outputs
loop to prevent unbounded growth.
There was a problem hiding this comment.
That's the user's choice.
There was a problem hiding this comment.
@jamesbhobbs Understood, acknowledged.
🧠 Learnings used
Learnt from: jankuca
Repo: deepnote/deepnote PR: 18381
File: libs/shared-node/src/blocks/notebooks.ts:818-833
Timestamp: 2025-09-11T09:39:56.743Z
Learning: Do not suggest adding whereNull('deleted_at') filters to the following_block and first_block queries in the getSiblingBlockSortingKeyPair function in libs/shared-node/src/blocks/notebooks.ts, as deleted blocks should be included in these lookups for correct sorting behavior.
Learnt from: CR
Repo: deepnote/deepnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T13:16:02.007Z
Learning: Applies to packages/blocks/src/**/*.{ts,tsx} : When making changes to `.deepnote` file format, schema, or block types, update `skills/deepnote/references/blocks-*.md` and `skills/deepnote/references/schema.ts`
Addresses review comment by @coderabbitai. Co-Authored-By: Claude <[email protected]>
Addresses review comment by @coderabbitai. Co-Authored-By: Claude <[email protected]>
Addresses review comment by @coderabbitai. Co-Authored-By: Claude <[email protected]>
Addresses review comment by @coderabbitai. Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/cli/src/commands/run.ts`:
- Around line 1003-1017: The current logic skips rendering final block outputs
when llmStreamed is set true for tool events but no text_delta arrives
(variables: llmStreamed, llmTextBuffer, result.outputs, renderOutput). Fix by
ensuring that after the streaming/text buffer handling you also render
result.outputs when they exist and haven't been printed: if llmStreamed is true
but llmTextBuffer is empty (or rendered was not written), iterate over
result.outputs and call renderOutput for each, and emit the trailing blank line
as done in the non-streamed branch; apply the same change to the other
equivalent block handling tool events so final outputs are never suppressed.
- Around line 248-253: The prompt-only run creates an in-memory convertedFile
with wasConverted: false which prevents materialization for --open; update the
convertedFile creation (where createPromptOnlyFile(...) is used and
convertedFile is assigned) to set wasConverted: true (or compute it from the
intent to open, e.g., options.open || true for prompt-only runs) so downstream
logic will write a temp file before upload/--open; ensure this change is applied
adjacent to the convertedFile assignment and consider using
absolutePath/workingDirectory as already computed so materialization succeeds
when isMachineOutput is false.
- Around line 193-208: The createLlmBlock function currently returns a
BlocksDeepnoteBlock and uses an unsafe "as any" cast; import the LlmBlock type
from "@deepnote/blocks", change the function signature to return LlmBlock (or
replace the return type with LlmBlock), remove the "as any" cast, and ensure the
constructed object matches the LlmBlock shape (id, blockGroup, sortingKey, type:
'llm', content, metadata, executionCount, outputs); adjust any property names if
needed so TypeScript accepts the object as LlmBlock without coercion.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b8d50fd9-babe-4508-a2ee-3492116da4ea
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
packages/cli/package.jsonpackages/cli/src/commands/run.tspackages/mcp/src/tools/writing.tspackages/runtime-core/src/llm-handler.ts
… output suppression - Import and use LlmBlock type to eliminate `as any` cast - Set wasConverted: true for prompt-only runs so --open materializes temp file - Change `else if (!llmStreamed)` to `else` so tool-only LLM runs still render outputs Addresses review comments by @coderabbitai. Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/cli/src/commands/run.ts`:
- Around line 472-474: The debug output currently prints safeOptions but only
masks token, exposing options.prompt; update the creation of safeOptions in
run.ts (where debug is called and safeOptions is built) to also redact any
prompt content by replacing options.prompt (and any similarly named prompt field
if present) with a placeholder like '[redacted]' before JSON.stringify; ensure
you still spread the rest of options so nothing else changes and keep using the
debug(`Options: ${JSON.stringify(safeOptions)}`) call.
- Around line 273-279: The code currently always uses lastNotebook to append the
created LLM block, causing prompts passed with --notebook to be added to the
wrong notebook; update the logic in the run command to pick the target notebook
based on the provided options.notebook (e.g., find the notebook from
file.project.notebooks by id or index matching options.notebook) and then call
createLlmBlock(options.prompt, targetNotebook.blocks.length) and push it into
targetNotebook.blocks; if options.notebook is provided but no matching notebook
is found, throw an explicit error (instead of falling back to the last notebook)
to prevent out-of-scope appends.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4e596c75-19b7-4c38-ba14-8b54a29b56fb
📒 Files selected for processing (1)
packages/cli/src/commands/run.ts
| if (path && options.prompt) { | ||
| const lastNotebook = file.project.notebooks[file.project.notebooks.length - 1] | ||
| if (lastNotebook) { | ||
| lastNotebook.blocks.push(createLlmBlock(options.prompt, lastNotebook.blocks.length)) | ||
| } else { | ||
| throw new Error('Cannot append prompt: file contains no notebooks') | ||
| } |
There was a problem hiding this comment.
Append prompt to the selected execution notebook, not always the last one.
When --notebook is set, Line 276 still appends the prompt block to the last notebook. That can make the prompt block out-of-scope and effectively ignored.
Proposed fix
if (path && options.prompt) {
- const lastNotebook = file.project.notebooks[file.project.notebooks.length - 1]
- if (lastNotebook) {
- lastNotebook.blocks.push(createLlmBlock(options.prompt, lastNotebook.blocks.length))
+ const targetNotebook = options.notebook
+ ? file.project.notebooks.find(notebook => notebook.name === options.notebook)
+ : file.project.notebooks[file.project.notebooks.length - 1]
+
+ if (targetNotebook) {
+ targetNotebook.blocks.push(createLlmBlock(options.prompt, targetNotebook.blocks.length))
+ } else if (options.notebook) {
+ throw new Error(`Cannot append prompt: notebook "${options.notebook}" not found`)
} else {
throw new Error('Cannot append prompt: file contains no notebooks')
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/run.ts` around lines 273 - 279, The code currently
always uses lastNotebook to append the created LLM block, causing prompts passed
with --notebook to be added to the wrong notebook; update the logic in the run
command to pick the target notebook based on the provided options.notebook
(e.g., find the notebook from file.project.notebooks by id or index matching
options.notebook) and then call createLlmBlock(options.prompt,
targetNotebook.blocks.length) and push it into targetNotebook.blocks; if
options.notebook is provided but no matching notebook is found, throw an
explicit error (instead of falling back to the last notebook) to prevent
out-of-scope appends.
| debug(`Running file: ${path ?? '(prompt-only)'}`) | ||
| const safeOptions = { ...options, token: options.token ? '[redacted]' : undefined } | ||
| debug(`Options: ${JSON.stringify(safeOptions)}`) |
There was a problem hiding this comment.
Redact prompt in debug logs.
Line 474 logs safeOptions, but only token is masked. Prompt text can contain sensitive data and is currently exposed in debug output.
Proposed fix
-const safeOptions = { ...options, token: options.token ? '[redacted]' : undefined }
+const safeOptions = {
+ ...options,
+ token: options.token ? '[redacted]' : undefined,
+ prompt: options.prompt ? '[redacted]' : undefined,
+}
debug(`Options: ${JSON.stringify(safeOptions)}`)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/run.ts` around lines 472 - 474, The debug output
currently prints safeOptions but only masks token, exposing options.prompt;
update the creation of safeOptions in run.ts (where debug is called and
safeOptions is built) to also redact any prompt content by replacing
options.prompt (and any similarly named prompt field if present) with a
placeholder like '[redacted]' before JSON.stringify; ensure you still spread the
rest of options so nothing else changes and keep using the debug(`Options:
${JSON.stringify(safeOptions)}`) call.
There was a problem hiding this comment.
Actionable comments posted: 7
♻️ Duplicate comments (1)
packages/runtime-core/src/agent-handler.ts (1)
129-134:⚠️ Potential issue | 🟠 Major
OPENAI_BASE_URLpath is currently unreachable due to the API key guard.Line 130 hard-fails without
OPENAI_API_KEY, but Lines 132-134 stateOPENAI_BASE_URLis an alternative. The contract is inconsistent; please align behavior and message.💡 Proposed fix
- const apiKey = process.env.OPENAI_API_KEY - if (!apiKey) { + const baseURL = process.env.OPENAI_BASE_URL + const apiKey = process.env.OPENAI_API_KEY ?? (baseURL ? 'unused' : undefined) + if (!apiKey) { throw new Error( - 'OPENAI_API_KEY environment variable is required for agent blocks.\n' + - 'Set it to your OpenAI API key, or set OPENAI_BASE_URL for compatible providers.' + 'OPENAI_API_KEY environment variable is required for agent blocks when OPENAI_BASE_URL is not set.' ) } @@ const client = new OpenAI({ apiKey, - baseURL: process.env.OPENAI_BASE_URL, + baseURL, })In the latest openai-node SDK, when a custom `baseURL` is set, is an `apiKey` still required, and is a placeholder key (e.g. "unused") supported for OpenAI-compatible providers?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` around lines 129 - 134, The current guard reads process.env.OPENAI_API_KEY into apiKey and throws if missing, but the error text claims OPENAI_BASE_URL is an alternative; change the guard to first check process.env.OPENAI_BASE_URL and only require OPENAI_API_KEY when OPENAI_BASE_URL is not set (or allow a placeholder key when base URL is provided if your SDK requires any value). Update the throw to reference both variables clearly and modify the check around the apiKey variable in agent-handler.ts (the apiKey constant and the thrown Error) so the code path that uses OPENAI_BASE_URL is reachable and the message accurately reflects the required configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/demo-agent-block.deepnote`:
- Around line 91-95: Update the npx invocation to pin the MCP server package to
a specific safe release: replace the unpinned package reference
'@modelcontextprotocol/server-filesystem' used in the npx args with a versioned
package specifier such as '@modelcontextprotocol/[email protected]'
(or a newer secure release) so the runtime installs a known, patched version;
locate the invocation that includes npx and the args array containing
'@modelcontextprotocol/server-filesystem' and './examples' and update that
package token accordingly.
In `@packages/blocks/src/blocks/agent-blocks.test.ts`:
- Around line 5-75: Add a negative test that asserts deepnoteBlockSchema rejects
an agent block when MCP server config is malformed: call
deepnoteBlockSchema.safeParse with an agent block where
metadata.deepnote_mcp_servers contains an invalid entry (e.g., missing the
required name field or wrong type for command), then assert
expect(result.success).toBe(false) and check result.error.issues includes the
path to metadata.deepnote_mcp_servers[0].name (or the offending field) to ensure
validation fails for that field; add this as a new it() test alongside the
existing agent tests.
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts`:
- Around line 272-274: The deepnote_max_iterations schema currently uses
z.number() which allows negatives and fractions; change the field definition for
deepnote_max_iterations to enforce a positive integer (e.g., use
z.number().int().min(1).default(10)) so only valid iteration counts are accepted
by the schema (update any tests/consumers that assume numeric type if needed).
In `@packages/cli/README.md`:
- Line 149: Update the `--prompt <text>` option description in the CLI README to
explicitly state that invoking this option will append or create an agent block
in the notebook (i.e., it mutates notebook state) rather than just “runs” an
agent block; locate the table entry for `--prompt <text>` in the README and
modify the right-hand description to include wording like “appends/creates an
agent block in the notebook (mutates notebook state); requires `OPENAI_API_KEY`”
so the side effect is clear.
- Around line 184-189: The README examples currently show inline secrets (the
commands containing "OPENAI_API_KEY=sk-... deepnote run my-project.deepnote
--prompt ..." and "OPENAI_API_KEY=sk-... deepnote run --prompt ..."); update
those examples to demonstrate exporting the key once (e.g., use a single-line
export instruction) and then run the deepnote run commands without embedding the
key, and add a brief note warning against pasting API keys into command history.
In `@packages/cli/src/commands/run.test.ts`:
- Around line 2536-2560: Add a new test case that uses setupSuccessfulRun(),
calls action(...) with a prompt containing quotes, newlines and unicode (e.g.
include " and \n and emojis) both for the in-memory path (undefined) and/or with
HELLO_WORLD_FILE, then assert mockRunProject was called once and inspect the
created file from mockRunProject.mock.calls[0] (same pattern as the existing
tests) to verify the last notebook block's type is 'agent' and its content
exactly matches the special-character prompt string; use the same helpers
(action, mockRunProject, setupSuccessfulRun, HELLO_WORLD_FILE) and assertions
style as the two existing tests to catch escaping/parsing regressions.
In `@packages/cli/src/commands/run.ts`:
- Around line 249-251: The synthetic prompt path uses process.cwd() instead of
the resolved execution cwd, so change the creation of absolutePath to use the
already-computed workingDirectory variable; update the expression that builds
absolutePath (used when constructing convertedFile with originalPath) to
join(workingDirectory, 'prompt.deepnote') so that --cwd is respected for
prompt-only synthetic paths (refer to variables absolutePath, workingDirectory,
convertedFile, file, originalPath).
---
Duplicate comments:
In `@packages/runtime-core/src/agent-handler.ts`:
- Around line 129-134: The current guard reads process.env.OPENAI_API_KEY into
apiKey and throws if missing, but the error text claims OPENAI_BASE_URL is an
alternative; change the guard to first check process.env.OPENAI_BASE_URL and
only require OPENAI_API_KEY when OPENAI_BASE_URL is not set (or allow a
placeholder key when base URL is provided if your SDK requires any value).
Update the throw to reference both variables clearly and modify the check around
the apiKey variable in agent-handler.ts (the apiKey constant and the thrown
Error) so the code path that uses OPENAI_BASE_URL is reachable and the message
accurately reflects the required configuration.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 944be19d-2463-487e-89e0-eec961035ee8
📒 Files selected for processing (24)
examples/README.mdexamples/demo-agent-block.deepnoteexamples/snapshots/agentic-analysis-demo_demo-agent-agentic_latest.snapshot.deepnotepackages/blocks/src/blocks/agent-blocks.test.tspackages/blocks/src/blocks/agent-blocks.tspackages/blocks/src/blocks/executable-blocks.tspackages/blocks/src/deepnote-file/agent-block-schema.test.tspackages/blocks/src/deepnote-file/deepnote-file-schema.tspackages/blocks/src/index.tspackages/blocks/src/python-code.test.tspackages/blocks/src/python-code.tspackages/cli/README.mdpackages/cli/src/commands/run.test.tspackages/cli/src/commands/run.tspackages/runtime-core/README.mdpackages/runtime-core/src/agent-handler.test.tspackages/runtime-core/src/agent-handler.tspackages/runtime-core/src/execution-engine.test.tspackages/runtime-core/src/execution-engine.tspackages/runtime-core/src/index.tsskills/deepnote/references/blocks-agent.mdskills/deepnote/references/cli-run.mdskills/deepnote/references/schema.tstest-fixtures/agent-block.deepnote
| command: npx | ||
| args: | ||
| - '-y' | ||
| - '@modelcontextprotocol/server-filesystem' | ||
| - ./examples |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
@modelcontextprotocol/server-filesystem npm latest version security advisories
💡 Result:
- Latest npm version (tagged “Version” by jsDelivr / mirrored on UNPKG):
2026.1.14. [1], [5] - Known security advisories (high severity) for
@modelcontextprotocol/server-filesystem:- Path validation bypass via colliding path prefix — GHSA-hc55-p739-j48w / CVE-2025-53110 (published July 1, 2025). [2], [3]
- Path validation bypass via symlink handling — GHSA-q66q-fx2p-7w4m / CVE-2025-53109 (published July 1, 2025). [2], [4]
- Fix version: advisories recommend upgrading to
2025.7.1or higher (so2026.1.14is beyond the fix line). [4]
Commands
npm view `@modelcontextprotocol/server-filesystem` version
npm audit
npm i `@modelcontextprotocol/server-filesystem`@latestSources: jsDelivr package page [1], GitHub security advisories list [2], GitLab advisory for CVE-2025-53110 [3], Vulert/NVD-linked CVE-2025-53109 summary [4], UNPKG listing for 2026.1.14 [5].
Pin the MCP server package to a specific secure version.
npx -y @modelcontextprotocol/server-filesystem`` without a pinned version pulls the latest at runtime, risking outdated or vulnerable versions. Two high-severity path validation CVEs (GHSA-hc55-p739-j48w, GHSA-q66q-fx2p-7w4m) affect versions before 2025.7.1. Pin to 2026.1.14 or newer.
Suggested fix
- - '@modelcontextprotocol/server-filesystem'
+ - '@modelcontextprotocol/[email protected]'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/demo-agent-block.deepnote` around lines 91 - 95, Update the npx
invocation to pin the MCP server package to a specific safe release: replace the
unpinned package reference '@modelcontextprotocol/server-filesystem' used in the
npx args with a versioned package specifier such as
'@modelcontextprotocol/[email protected]' (or a newer secure release)
so the runtime installs a known, patched version; locate the invocation that
includes npx and the args array containing
'@modelcontextprotocol/server-filesystem' and './examples' and update that
package token accordingly.
| describe('agent block schema', () => { | ||
| it('parses a minimal agent block', () => { | ||
| const result = deepnoteBlockSchema.safeParse({ | ||
| id: 'abc123', | ||
| blockGroup: 'grp123', | ||
| sortingKey: 'a0', | ||
| type: 'agent', | ||
| content: 'Analyze the data', | ||
| metadata: {}, | ||
| executionCount: null, | ||
| outputs: [], | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| if (result.success) { | ||
| expect(result.data.type).toBe('agent') | ||
| expect(result.data.content).toBe('Analyze the data') | ||
| } | ||
| }) | ||
|
|
||
| it('applies default metadata values', () => { | ||
| const result = deepnoteBlockSchema.safeParse({ | ||
| id: 'abc123', | ||
| blockGroup: 'grp123', | ||
| sortingKey: 'a0', | ||
| type: 'agent', | ||
| content: 'test prompt', | ||
| executionCount: null, | ||
| outputs: [], | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| if (result.success && result.data.type === 'agent') { | ||
| expect(result.data.metadata.deepnote_model).toBe('auto') | ||
| expect(result.data.metadata.deepnote_max_iterations).toBe(10) | ||
| } | ||
| }) | ||
|
|
||
| it('parses agent block with MCP servers', () => { | ||
| const result = deepnoteBlockSchema.safeParse({ | ||
| id: 'abc123', | ||
| blockGroup: 'grp123', | ||
| sortingKey: 'a0', | ||
| type: 'agent', | ||
| content: 'query the db', | ||
| metadata: { | ||
| deepnote_model: 'gpt-4o-mini', | ||
| deepnote_max_iterations: 5, | ||
| deepnote_mcp_servers: [ | ||
| { | ||
| name: 'postgres', | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-postgres'], | ||
| // biome-ignore lint/suspicious/noTemplateCurlyInString: testing env var reference syntax | ||
| env: { DATABASE_URL: '${DATABASE_URL}' }, | ||
| }, | ||
| ], | ||
| }, | ||
| executionCount: null, | ||
| outputs: [], | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| if (result.success && result.data.type === 'agent') { | ||
| expect(result.data.metadata.deepnote_model).toBe('gpt-4o-mini') | ||
| expect(result.data.metadata.deepnote_max_iterations).toBe(5) | ||
| expect(result.data.metadata.deepnote_mcp_servers).toHaveLength(1) | ||
| expect(result.data.metadata.deepnote_mcp_servers?.[0]?.name).toBe('postgres') | ||
| } | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Add at least one invalid-schema assertion for agent metadata.
Current cases are all success-path. Add a failing MCP config case so schema regressions in validation are caught.
Suggested negative test
describe('agent block schema', () => {
+ it('rejects invalid MCP server config', () => {
+ const result = deepnoteBlockSchema.safeParse({
+ id: 'abc123',
+ blockGroup: 'grp123',
+ sortingKey: 'a0',
+ type: 'agent',
+ content: 'query the db',
+ metadata: {
+ deepnote_mcp_servers: [{ name: 'postgres' }], // missing required command
+ },
+ executionCount: null,
+ outputs: [],
+ })
+
+ expect(result.success).toBe(false)
+ })
+
it('parses a minimal agent block', () => {As per coding guidelines: "Test edge cases, error handling, and special characters in test files."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/blocks/agent-blocks.test.ts` around lines 5 - 75, Add a
negative test that asserts deepnoteBlockSchema rejects an agent block when MCP
server config is malformed: call deepnoteBlockSchema.safeParse with an agent
block where metadata.deepnote_mcp_servers contains an invalid entry (e.g.,
missing the required name field or wrong type for command), then assert
expect(result.success).toBe(false) and check result.error.issues includes the
path to metadata.deepnote_mcp_servers[0].name (or the offending field) to ensure
validation fails for that field; add this as a new it() test alongside the
existing agent tests.
| deepnote_model: z.string().default('auto'), | ||
| deepnote_max_iterations: z.number().default(10), | ||
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), |
There was a problem hiding this comment.
Constrain deepnote_max_iterations to valid iteration semantics.
z.number() accepts negative and fractional values. That can propagate invalid iteration counts into agent execution behavior.
Suggested fix
- deepnote_max_iterations: z.number().default(10),
+ deepnote_max_iterations: z.number().int().min(1).default(10),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| deepnote_model: z.string().default('auto'), | |
| deepnote_max_iterations: z.number().default(10), | |
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), | |
| deepnote_model: z.string().default('auto'), | |
| deepnote_max_iterations: z.number().int().min(1).default(10), | |
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts` around lines 272 -
274, The deepnote_max_iterations schema currently uses z.number() which allows
negatives and fractions; change the field definition for deepnote_max_iterations
to enforce a positive integer (e.g., use z.number().int().min(1).default(10)) so
only valid iteration counts are accepted by the schema (update any
tests/consumers that assume numeric type if needed).
| | `--block <id>` | Run only the specified block | all blocks | | ||
| | `-i, --input <key=val>` | Set input variable value (can be repeated) | | | ||
| | `--list-inputs` | List input variables without running | `false` | | ||
| | `--prompt <text>` | Run an LLM agent block with the given prompt (requires `OPENAI_API_KEY`) | | |
There was a problem hiding this comment.
Call out file mutation in the option description.
Line 149 should explicitly say this appends/creates an agent block (mutates notebook state), not just “runs” one. That side effect is easy to miss in the table view.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/README.md` at line 149, Update the `--prompt <text>` option
description in the CLI README to explicitly state that invoking this option will
append or create an agent block in the notebook (i.e., it mutates notebook
state) rather than just “runs” an agent block; locate the table entry for
`--prompt <text>` in the README and modify the right-hand description to include
wording like “appends/creates an agent block in the notebook (mutates notebook
state); requires `OPENAI_API_KEY`” so the side effect is clear.
| # Run an agent with a prompt (appends an agent block to the file) | ||
| OPENAI_API_KEY=sk-... deepnote run my-project.deepnote --prompt "Analyze the sales data" | ||
|
|
||
| # Run an agent block standalone (no file needed) | ||
| OPENAI_API_KEY=sk-... deepnote run --prompt "Write a hello world script" | ||
| ``` |
There was a problem hiding this comment.
Avoid inline secret usage in examples.
Lines 185 and 188 normalize pasting API keys into command history. Prefer export OPENAI_API_KEY=... once, then run commands without embedding secrets inline.
Suggested doc tweak
-# Run an agent with a prompt (appends an agent block to the file)
-OPENAI_API_KEY=sk-... deepnote run my-project.deepnote --prompt "Analyze the sales data"
+# Run an agent with a prompt (appends an agent block to the file)
+export OPENAI_API_KEY="sk-..."
+deepnote run my-project.deepnote --prompt "Analyze the sales data"
-# Run an agent block standalone (no file needed)
-OPENAI_API_KEY=sk-... deepnote run --prompt "Write a hello world script"
+# Run an agent block standalone (no file needed)
+deepnote run --prompt "Write a hello world script"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Run an agent with a prompt (appends an agent block to the file) | |
| OPENAI_API_KEY=sk-... deepnote run my-project.deepnote --prompt "Analyze the sales data" | |
| # Run an agent block standalone (no file needed) | |
| OPENAI_API_KEY=sk-... deepnote run --prompt "Write a hello world script" | |
| ``` | |
| # Run an agent with a prompt (appends an agent block to the file) | |
| export OPENAI_API_KEY="sk-..." | |
| deepnote run my-project.deepnote --prompt "Analyze the sales data" | |
| # Run an agent block standalone (no file needed) | |
| deepnote run --prompt "Write a hello world script" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/README.md` around lines 184 - 189, The README examples currently
show inline secrets (the commands containing "OPENAI_API_KEY=sk-... deepnote run
my-project.deepnote --prompt ..." and "OPENAI_API_KEY=sk-... deepnote run
--prompt ..."); update those examples to demonstrate exporting the key once
(e.g., use a single-line export instruction) and then run the deepnote run
commands without embedding the key, and add a brief note warning against pasting
API keys into command history.
| it('creates in-memory file and runs with --prompt and no path', async () => { | ||
| setupSuccessfulRun() | ||
|
|
||
| await action(undefined, { prompt: 'Say hello' }) | ||
|
|
||
| expect(mockRunProject).toHaveBeenCalledTimes(1) | ||
| const [file] = mockRunProject.mock.calls[0] | ||
| expect(file.project.notebooks).toHaveLength(1) | ||
| expect(file.project.notebooks[0].blocks).toHaveLength(1) | ||
| expect(file.project.notebooks[0].blocks[0].type).toBe('agent') | ||
| expect(file.project.notebooks[0].blocks[0].content).toBe('Say hello') | ||
| }) | ||
|
|
||
| it('appends agent block to existing file with --prompt and path', async () => { | ||
| setupSuccessfulRun() | ||
|
|
||
| await action(HELLO_WORLD_FILE, { prompt: 'Analyze data' }) | ||
|
|
||
| expect(mockRunProject).toHaveBeenCalledTimes(1) | ||
| const [file] = mockRunProject.mock.calls[0] | ||
| const blocks = file.project.notebooks[file.project.notebooks.length - 1].blocks | ||
| const lastBlock = blocks[blocks.length - 1] | ||
| expect(lastBlock.type).toBe('agent') | ||
| expect(lastBlock.content).toBe('Analyze data') | ||
| }) |
There was a problem hiding this comment.
Add a special-character prompt test for --prompt.
Line 2539 and Line 2552 only exercise simple prompts. Add one case with quotes/newlines/unicode to catch escaping/parsing regressions.
Suggested test addition
it('appends agent block to existing file with --prompt and path', async () => {
setupSuccessfulRun()
await action(HELLO_WORLD_FILE, { prompt: 'Analyze data' })
expect(mockRunProject).toHaveBeenCalledTimes(1)
const [file] = mockRunProject.mock.calls[0]
const blocks = file.project.notebooks[file.project.notebooks.length - 1].blocks
const lastBlock = blocks[blocks.length - 1]
expect(lastBlock.type).toBe('agent')
expect(lastBlock.content).toBe('Analyze data')
})
+
+ it('preserves special characters in prompt content', async () => {
+ setupSuccessfulRun()
+ const prompt = 'Summarize "Q1"\nLine 2 — café ☕'
+
+ await action(undefined, { prompt })
+
+ const [file] = mockRunProject.mock.calls[0]
+ expect(file.project.notebooks[0].blocks[0].type).toBe('agent')
+ expect(file.project.notebooks[0].blocks[0].content).toBe(prompt)
+ })As per coding guidelines: "Test edge cases, error handling, and special characters in test files."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('creates in-memory file and runs with --prompt and no path', async () => { | |
| setupSuccessfulRun() | |
| await action(undefined, { prompt: 'Say hello' }) | |
| expect(mockRunProject).toHaveBeenCalledTimes(1) | |
| const [file] = mockRunProject.mock.calls[0] | |
| expect(file.project.notebooks).toHaveLength(1) | |
| expect(file.project.notebooks[0].blocks).toHaveLength(1) | |
| expect(file.project.notebooks[0].blocks[0].type).toBe('agent') | |
| expect(file.project.notebooks[0].blocks[0].content).toBe('Say hello') | |
| }) | |
| it('appends agent block to existing file with --prompt and path', async () => { | |
| setupSuccessfulRun() | |
| await action(HELLO_WORLD_FILE, { prompt: 'Analyze data' }) | |
| expect(mockRunProject).toHaveBeenCalledTimes(1) | |
| const [file] = mockRunProject.mock.calls[0] | |
| const blocks = file.project.notebooks[file.project.notebooks.length - 1].blocks | |
| const lastBlock = blocks[blocks.length - 1] | |
| expect(lastBlock.type).toBe('agent') | |
| expect(lastBlock.content).toBe('Analyze data') | |
| }) | |
| it('creates in-memory file and runs with --prompt and no path', async () => { | |
| setupSuccessfulRun() | |
| await action(undefined, { prompt: 'Say hello' }) | |
| expect(mockRunProject).toHaveBeenCalledTimes(1) | |
| const [file] = mockRunProject.mock.calls[0] | |
| expect(file.project.notebooks).toHaveLength(1) | |
| expect(file.project.notebooks[0].blocks).toHaveLength(1) | |
| expect(file.project.notebooks[0].blocks[0].type).toBe('agent') | |
| expect(file.project.notebooks[0].blocks[0].content).toBe('Say hello') | |
| }) | |
| it('appends agent block to existing file with --prompt and path', async () => { | |
| setupSuccessfulRun() | |
| await action(HELLO_WORLD_FILE, { prompt: 'Analyze data' }) | |
| expect(mockRunProject).toHaveBeenCalledTimes(1) | |
| const [file] = mockRunProject.mock.calls[0] | |
| const blocks = file.project.notebooks[file.project.notebooks.length - 1].blocks | |
| const lastBlock = blocks[blocks.length - 1] | |
| expect(lastBlock.type).toBe('agent') | |
| expect(lastBlock.content).toBe('Analyze data') | |
| }) | |
| it('preserves special characters in prompt content', async () => { | |
| setupSuccessfulRun() | |
| const prompt = 'Summarize "Q1"\nLine 2 — café ☕' | |
| await action(undefined, { prompt }) | |
| const [file] = mockRunProject.mock.calls[0] | |
| expect(file.project.notebooks[0].blocks[0].type).toBe('agent') | |
| expect(file.project.notebooks[0].blocks[0].content).toBe(prompt) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/run.test.ts` around lines 2536 - 2560, Add a new
test case that uses setupSuccessfulRun(), calls action(...) with a prompt
containing quotes, newlines and unicode (e.g. include " and \n and emojis) both
for the in-memory path (undefined) and/or with HELLO_WORLD_FILE, then assert
mockRunProject was called once and inspect the created file from
mockRunProject.mock.calls[0] (same pattern as the existing tests) to verify the
last notebook block's type is 'agent' and its content exactly matches the
special-character prompt string; use the same helpers (action, mockRunProject,
setupSuccessfulRun, HELLO_WORLD_FILE) and assertions style as the two existing
tests to catch escaping/parsing regressions.
| absolutePath = join(process.cwd(), 'prompt.deepnote') | ||
| workingDirectory = options.cwd ?? process.cwd() | ||
| convertedFile = { file, originalPath: absolutePath, format: 'deepnote', wasConverted: true } |
There was a problem hiding this comment.
Use workingDirectory for prompt-only synthetic path.
Line 249 currently ignores --cwd; snapshot/open paths can resolve to the caller’s CWD instead of the execution CWD.
Suggested fix
- absolutePath = join(process.cwd(), 'prompt.deepnote')
- workingDirectory = options.cwd ?? process.cwd()
+ workingDirectory = options.cwd ?? process.cwd()
+ absolutePath = join(workingDirectory, 'prompt.deepnote')🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/cli/src/commands/run.ts` around lines 249 - 251, The synthetic
prompt path uses process.cwd() instead of the resolved execution cwd, so change
the creation of absolutePath to use the already-computed workingDirectory
variable; update the expression that builds absolutePath (used when constructing
convertedFile with originalPath) to join(workingDirectory, 'prompt.deepnote') so
that --cwd is respected for prompt-only synthetic paths (refer to variables
absolutePath, workingDirectory, convertedFile, file, originalPath).
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/blocks/src/blocks/executable-blocks.ts (1)
18-28:⚠️ Potential issue | 🟡 MinorRemove the duplicate
'agent'entry.
Setwill dedupe this at runtime, but the second'agent'looks accidental and makes it harder to tell whether another block type was meant to be added here. As per coding guidelines, "Keep code clean and readable following Biome guidelines".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/blocks/src/blocks/executable-blocks.ts` around lines 18 - 28, The array used to construct the executableBlockTypes Set contains a duplicate 'agent' entry which is accidental and should be removed; edit the initializer for executableBlockTypes (the Set literal in blocks/executable-blocks.ts) to delete the second 'agent' string so the list only contains unique block types (and quickly scan the surrounding entries like 'code', 'sql', 'notebook-function', 'visualization', 'button', 'big-number' and ...INPUT_BLOCK_TYPES to ensure no other duplicates or a missing intended type).packages/blocks/src/deepnote-file/deepnote-file-schema.ts (1)
475-484: 🧹 Nitpick | 🔵 TrivialRemove the duplicated
AgentBlockunion member.
ExecutableBlockalready includesAgentBlockearlier in the union, so the extra entry just reads like a merge artifact.Proposed fix
export type ExecutableBlock = | AgentBlock | CodeBlock | SqlBlock | NotebookFunctionBlock | VisualizationBlock | ButtonBlock | BigNumberBlock - | AgentBlock | InputBlock🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts` around lines 475 - 484, The union type ExecutableBlock contains a duplicated member AgentBlock; edit the ExecutableBlock type declaration to remove the redundant "AgentBlock" entry so each variant appears only once (locate the ExecutableBlock type and delete the second AgentBlock union member).packages/runtime-core/src/execution-engine.ts (1)
18-27:⚠️ Potential issue | 🟡 MinorRemove the duplicate
'agent'entry from the exported list.
executableBlockTypeSetmasks this internally, but callers ofexecutableBlockTypeswill still see'agent'twice.Suggested fix
export const executableBlockTypes: ExecutableBlock['type'][] = [ 'agent', 'code', 'sql', 'notebook-function', 'visualization', 'button', 'big-number', - 'agent', 'input-text', 'input-textarea', 'input-checkbox',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/execution-engine.ts` around lines 18 - 27, The exported array executableBlockTypes contains a duplicated 'agent' entry which causes callers to see it twice; remove the duplicate string from the array literal so each ExecutableBlock['type'] appears only once (update the exported executableBlockTypes definition to contain a single 'agent' element).
♻️ Duplicate comments (3)
packages/cli/src/commands/run.ts (2)
462-473:⚠️ Potential issue | 🟠 MajorRedact
promptin debug logs.
safeOptionsonly maskstoken, so a--promptvalue is now serialized verbatim into debug output. Prompts often carry notebook data or credentials.Suggested fix
- const safeOptions = { ...options, token: options.token ? '[redacted]' : undefined } + const safeOptions = { + ...options, + token: options.token ? '[redacted]' : undefined, + prompt: options.prompt ? '[redacted]' : undefined, + } debug(`Options: ${JSON.stringify(safeOptions)}`)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/run.ts` around lines 462 - 473, The debug serialization exposes sensitive prompt content because safeOptions only masks token; update createRunAction to also redact options.prompt before logging by creating safeOptions from options with token replaced by '[redacted]' when present and prompt replaced by '[redacted]' when present (leave other fields unchanged), then use JSON.stringify(safeOptions) in the debug call so neither prompt nor token appear in logs.
272-278:⚠️ Potential issue | 🟠 MajorAppend the synthetic agent block to the execution notebook.
When
--notebookis set, this still pushes the prompt block into the last notebook. The run can then execute a different notebook and skip the prompt entirely.Suggested fix
if (path && options.prompt) { - const lastNotebook = file.project.notebooks[file.project.notebooks.length - 1] - if (lastNotebook) { - lastNotebook.blocks.push(createAgentBlock(options.prompt, lastNotebook.blocks.length)) + const targetNotebook = options.notebook + ? file.project.notebooks.find(notebook => notebook.name === options.notebook) + : file.project.notebooks[file.project.notebooks.length - 1] + + if (targetNotebook) { + targetNotebook.blocks.push(createAgentBlock(options.prompt, targetNotebook.blocks.length)) + } else if (options.notebook) { + throw new Error(`Cannot append prompt: notebook "${options.notebook}" not found`) } else { throw new Error('Cannot append prompt: file contains no notebooks') } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/run.ts` around lines 272 - 278, The code appends the synthetic agent block to the last notebook (file.project.notebooks[file.project.notebooks.length - 1]) which ignores a user-selected notebook via the --notebook option; change the logic to compute the intended execution notebook (use the existing notebook selection variable or option, e.g., a notebookIndex/notebookName passed in via path/options) and append the block to that notebook (e.g., resolve targetNotebook = file.project.notebooks[notebookIndex] or find by name) instead of lastNotebook; update the push call to targetNotebook.blocks.push(createAgentBlock(options.prompt, targetNotebook.blocks.length)) and throw an error if the resolved targetNotebook is missing.packages/runtime-core/src/agent-handler.ts (1)
38-40:⚠️ Potential issue | 🟠 MajorThis sorting-key generator is incompatible with the notebook formats already in the repo.
The repository already has blocks keyed as
a0/a1and others keyed as000004-style strings, while execution later sorts lexicographically bysortingKey. Hard-codinga00000003can move agent-inserted blocks ahead of legacya1/a2blocks or behind numeric-key blocks on the next run.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` around lines 38 - 40, The generateSortingKey function currently zero-pads and creates keys like "a00000003", which breaks compatibility with existing notebook keys ("a0"/"a1" and numeric "000004" styles) and causes incorrect lexicographic ordering; change generateSortingKey to produce compact keys that preserve legacy grouping by returning "a" + index.toString() (no pad) so agent-inserted blocks remain in the expected "a" bucket and do not leapfrog numeric-only keys when sorted by sortingKey.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/demo-agent-block.deepnote`:
- Around line 80-94: Step 6 currently tells the agent to save the report as
`examples/report.md` which will be resolved relative to the filesystem MCP root
(already `./examples`) and produce `./examples/examples/report.md`; update the
instruction to write the file path relative to the MCP root (e.g., `report.md`
or `./report.md`) so the filesystem MCP tool saves the report at
`./examples/report.md`; ensure the task text referencing the filesystem MCP
server (metadata.deepnote_mcp_servers entry) and the step that calls the
filesystem MCP tool reflect this corrected relative path.
In `@packages/blocks/src/blocks/agent-blocks.test.ts`:
- Around line 42-70: The test 'parses agent block with MCP servers' currently
only asserts the MCP server name allowing regressions that drop other fields;
update the assertions after deepnoteBlockSchema.safeParse to also verify
deepnote_mcp_servers[0].command equals 'npx', deepnote_mcp_servers[0].args
equals ['-y','@modelcontextprotocol/server-postgres'], and
deepnote_mcp_servers[0].env.DATABASE_URL contains the '${DATABASE_URL}'
placeholder (or exact string), so the parser is validated to preserve command,
args, and env fields.
In `@packages/blocks/src/deepnote-file/agent-block-schema.test.ts`:
- Around line 29-49: The test currently only asserts the length of
project.settings.mcpServers after parsing with deepnoteFileSchema, which can
miss shape regressions; update the test to assert the parsed MCP entries' fields
(for example verify each entry's name, command, args, and env where provided)
against the expected values so the schema actually preserves command/args/env
(use deepnoteFileSchema.safeParse result.data.project.settings?.mcpServers and
assert entries equal or match the expected objects).
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts`:
- Around line 253-258: The schema mcpServerSchema currently uses z.string() for
the name and command fields which allows empty or whitespace-only values; update
those fields to trim whitespace and enforce at least one character, e.g. replace
name: z.string() and command: z.string() with name: z.string().trim().min(1) and
command: z.string().trim().min(1) so blank or whitespace-only server
names/commands are rejected at validation time.
In `@packages/blocks/src/python-code.test.ts`:
- Around line 984-1000: Replace the duplicate happy-path test in the "Agent
blocks" suite with an edge-case that verifies createPythonCode handles special
characters; update the it(...) case that currently builds an AgentBlock (id
'123', type 'agent', content 'Analyze the data', metadata.deepnote_agent_model
'gpt-4o') to instead use an AgentBlock whose content contains special characters
(quotes, newlines, non-ASCII, and comment-like sequences) and assert
createPythonCode returns properly escaped/commented Python output; locate the
test inside the describe('Agent blocks') block and modify the AgentBlock fixture
and expected result to validate correct handling by createPythonCode.
In `@packages/runtime-core/src/agent-handler.ts`:
- Around line 12-21: AgentBlockContext currently lacks any working-directory
info so relative MCP roots like "./examples" are forwarded unchanged; add a
workingDirectory (string) field to AgentBlockContext and update the code paths
that forward/consume MCP roots (the handler that forwards roots and the code
referenced around the same area) to resolve relative roots against this
workingDirectory using path.resolve(workingDirectory, root) (leave absolute
paths untouched). Ensure the new workingDirectory is threaded through wherever
AgentBlockContext is constructed so consumers use the resolved absolute paths.
- Around line 190-199: The created code block (newBlock) is never updated with
the agent execution results, so newBlock.outputs and newBlock.executionCount
remain empty while results are stored only in collectedOutputs; update newBlock
right after execution by assigning newBlock.outputs =
cloneDeep(collectedOutputs[newBlock.id] || collectedOutputs[correspondingId] ||
executionResult.outputs) and newBlock.executionCount =
executionResult.executionCount || (Array.isArray(newBlock.outputs) ?
newBlock.outputs.length ? (newBlock.outputs[0].execution_count ?? null) : null :
null); ensure you use the same identifiers used when mirroring results
(collectedOutputs, executionResult) and deep-copy the outputs before assigning
so saved notebooks/snapshots persist the executed outputs; apply the same fix in
the other insertion branch that mirrors results (the block handling the
alternate insert path).
---
Outside diff comments:
In `@packages/blocks/src/blocks/executable-blocks.ts`:
- Around line 18-28: The array used to construct the executableBlockTypes Set
contains a duplicate 'agent' entry which is accidental and should be removed;
edit the initializer for executableBlockTypes (the Set literal in
blocks/executable-blocks.ts) to delete the second 'agent' string so the list
only contains unique block types (and quickly scan the surrounding entries like
'code', 'sql', 'notebook-function', 'visualization', 'button', 'big-number' and
...INPUT_BLOCK_TYPES to ensure no other duplicates or a missing intended type).
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts`:
- Around line 475-484: The union type ExecutableBlock contains a duplicated
member AgentBlock; edit the ExecutableBlock type declaration to remove the
redundant "AgentBlock" entry so each variant appears only once (locate the
ExecutableBlock type and delete the second AgentBlock union member).
In `@packages/runtime-core/src/execution-engine.ts`:
- Around line 18-27: The exported array executableBlockTypes contains a
duplicated 'agent' entry which causes callers to see it twice; remove the
duplicate string from the array literal so each ExecutableBlock['type'] appears
only once (update the exported executableBlockTypes definition to contain a
single 'agent' element).
---
Duplicate comments:
In `@packages/cli/src/commands/run.ts`:
- Around line 462-473: The debug serialization exposes sensitive prompt content
because safeOptions only masks token; update createRunAction to also redact
options.prompt before logging by creating safeOptions from options with token
replaced by '[redacted]' when present and prompt replaced by '[redacted]' when
present (leave other fields unchanged), then use JSON.stringify(safeOptions) in
the debug call so neither prompt nor token appear in logs.
- Around line 272-278: The code appends the synthetic agent block to the last
notebook (file.project.notebooks[file.project.notebooks.length - 1]) which
ignores a user-selected notebook via the --notebook option; change the logic to
compute the intended execution notebook (use the existing notebook selection
variable or option, e.g., a notebookIndex/notebookName passed in via
path/options) and append the block to that notebook (e.g., resolve
targetNotebook = file.project.notebooks[notebookIndex] or find by name) instead
of lastNotebook; update the push call to
targetNotebook.blocks.push(createAgentBlock(options.prompt,
targetNotebook.blocks.length)) and throw an error if the resolved targetNotebook
is missing.
In `@packages/runtime-core/src/agent-handler.ts`:
- Around line 38-40: The generateSortingKey function currently zero-pads and
creates keys like "a00000003", which breaks compatibility with existing notebook
keys ("a0"/"a1" and numeric "000004" styles) and causes incorrect lexicographic
ordering; change generateSortingKey to produce compact keys that preserve legacy
grouping by returning "a" + index.toString() (no pad) so agent-inserted blocks
remain in the expected "a" bucket and do not leapfrog numeric-only keys when
sorted by sortingKey.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: df087efe-227d-45af-b54e-ed50197e8fcc
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (19)
examples/demo-agent-block.deepnoteexamples/snapshots/agentic-analysis-demo_demo-agent-agentic_latest.snapshot.deepnotepackages/blocks/src/blocks/agent-blocks.test.tspackages/blocks/src/blocks/executable-blocks.tspackages/blocks/src/deepnote-file/agent-block-schema.test.tspackages/blocks/src/deepnote-file/deepnote-file-schema.tspackages/blocks/src/python-code.test.tspackages/cli/package.jsonpackages/cli/src/commands/run.tspackages/cli/src/utils/block-label.test.tspackages/convert/src/roundtrip-all-formats.test.tspackages/runtime-core/package.jsonpackages/runtime-core/src/agent-handler.tspackages/runtime-core/src/execution-engine.tspackages/runtime-core/src/index.tsskills/deepnote/references/blocks-agent.mdskills/deepnote/references/cli-run.mdskills/deepnote/references/schema.tstest-fixtures/agent-block.deepnote
| 6. Use the filesystem MCP tool to save a final executive report as `examples/report.md` | ||
| 7. Add a final markdown block with an executive summary containing 3 key takeaways and a recommendation | ||
|
|
||
| Use clean, publication-quality matplotlib charts with proper labels and titles. | ||
| Use `plt.tight_layout()` and `plt.savefig()` for each chart. | ||
| id: a0000000000000000000000000000004 | ||
| metadata: | ||
| deepnote_agent_model: gpt-4o | ||
| deepnote_mcp_servers: | ||
| - name: filesystem | ||
| command: npx | ||
| args: | ||
| - '-y' | ||
| - '@modelcontextprotocol/server-filesystem' | ||
| - ./examples |
There was a problem hiding this comment.
Write the report path relative to the MCP root.
The filesystem server is already rooted at ./examples, so asking the agent to save examples/report.md targets a nested path instead of the intended file. This makes step 6 write to ./examples/examples/report.md or fail.
Suggested fix
- 6. Use the filesystem MCP tool to save a final executive report as `examples/report.md`
+ 6. Use the filesystem MCP tool to save a final executive report as `report.md`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/demo-agent-block.deepnote` around lines 80 - 94, Step 6 currently
tells the agent to save the report as `examples/report.md` which will be
resolved relative to the filesystem MCP root (already `./examples`) and produce
`./examples/examples/report.md`; update the instruction to write the file path
relative to the MCP root (e.g., `report.md` or `./report.md`) so the filesystem
MCP tool saves the report at `./examples/report.md`; ensure the task text
referencing the filesystem MCP server (metadata.deepnote_mcp_servers entry) and
the step that calls the filesystem MCP tool reflect this corrected relative
path.
| it('parses agent block with MCP servers', () => { | ||
| const result = deepnoteBlockSchema.safeParse({ | ||
| id: 'abc123', | ||
| blockGroup: 'grp123', | ||
| sortingKey: 'a0', | ||
| type: 'agent', | ||
| content: 'query the db', | ||
| metadata: { | ||
| deepnote_agent_model: 'gpt-4o-mini', | ||
| deepnote_mcp_servers: [ | ||
| { | ||
| name: 'postgres', | ||
| command: 'npx', | ||
| args: ['-y', '@modelcontextprotocol/server-postgres'], | ||
| // biome-ignore lint/suspicious/noTemplateCurlyInString: testing env var reference syntax | ||
| env: { DATABASE_URL: '${DATABASE_URL}' }, | ||
| }, | ||
| ], | ||
| }, | ||
| executionCount: null, | ||
| outputs: [], | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| if (result.success && result.data.type === 'agent') { | ||
| expect(result.data.metadata.deepnote_agent_model).toBe('gpt-4o-mini') | ||
| expect(result.data.metadata.deepnote_mcp_servers).toHaveLength(1) | ||
| expect(result.data.metadata.deepnote_mcp_servers?.[0]?.name).toBe('postgres') | ||
| } |
There was a problem hiding this comment.
Assert the MCP fields you construct.
This case adds command, args, and a ${DATABASE_URL} placeholder, but only name is asserted. A parser that drops or rewrites the new fields still passes.
Proposed fix
if (result.success && result.data.type === 'agent') {
expect(result.data.metadata.deepnote_agent_model).toBe('gpt-4o-mini')
expect(result.data.metadata.deepnote_mcp_servers).toHaveLength(1)
- expect(result.data.metadata.deepnote_mcp_servers?.[0]?.name).toBe('postgres')
+ expect(result.data.metadata.deepnote_mcp_servers?.[0]).toMatchObject({
+ name: 'postgres',
+ command: 'npx',
+ args: ['-y', '@modelcontextprotocol/server-postgres'],
+ env: { DATABASE_URL: '${DATABASE_URL}' },
+ })
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/blocks/agent-blocks.test.ts` around lines 42 - 70, The
test 'parses agent block with MCP servers' currently only asserts the MCP server
name allowing regressions that drop other fields; update the assertions after
deepnoteBlockSchema.safeParse to also verify deepnote_mcp_servers[0].command
equals 'npx', deepnote_mcp_servers[0].args equals
['-y','@modelcontextprotocol/server-postgres'], and
deepnote_mcp_servers[0].env.DATABASE_URL contains the '${DATABASE_URL}'
placeholder (or exact string), so the parser is validated to preserve command,
args, and env fields.
| it('validates project-level mcpServers in settings', () => { | ||
| const result = deepnoteFileSchema.safeParse({ | ||
| version: '1.0.0', | ||
| metadata: { createdAt: '2026-01-01T00:00:00Z' }, | ||
| project: { | ||
| id: 'test', | ||
| name: 'Test', | ||
| settings: { | ||
| mcpServers: [ | ||
| { name: 'test-server', command: 'python', args: ['-m', 'server'] }, | ||
| { name: 'another', command: 'npx', env: { KEY: 'val' } }, | ||
| ], | ||
| }, | ||
| notebooks: [], | ||
| }, | ||
| }) | ||
|
|
||
| expect(result.success).toBe(true) | ||
| if (result.success) { | ||
| expect(result.data.project.settings?.mcpServers).toHaveLength(2) | ||
| } |
There was a problem hiding this comment.
Assert the parsed MCP fields, not just the count.
This only checks length. If parsing strips command or env from project settings, the test still passes and misses the new shape.
Proposed fix
expect(result.success).toBe(true)
if (result.success) {
expect(result.data.project.settings?.mcpServers).toHaveLength(2)
+ expect(result.data.project.settings?.mcpServers?.[0]).toMatchObject({
+ name: 'test-server',
+ command: 'python',
+ args: ['-m', 'server'],
+ })
+ expect(result.data.project.settings?.mcpServers?.[1]).toMatchObject({
+ name: 'another',
+ command: 'npx',
+ env: { KEY: 'val' },
+ })
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| it('validates project-level mcpServers in settings', () => { | |
| const result = deepnoteFileSchema.safeParse({ | |
| version: '1.0.0', | |
| metadata: { createdAt: '2026-01-01T00:00:00Z' }, | |
| project: { | |
| id: 'test', | |
| name: 'Test', | |
| settings: { | |
| mcpServers: [ | |
| { name: 'test-server', command: 'python', args: ['-m', 'server'] }, | |
| { name: 'another', command: 'npx', env: { KEY: 'val' } }, | |
| ], | |
| }, | |
| notebooks: [], | |
| }, | |
| }) | |
| expect(result.success).toBe(true) | |
| if (result.success) { | |
| expect(result.data.project.settings?.mcpServers).toHaveLength(2) | |
| } | |
| it('validates project-level mcpServers in settings', () => { | |
| const result = deepnoteFileSchema.safeParse({ | |
| version: '1.0.0', | |
| metadata: { createdAt: '2026-01-01T00:00:00Z' }, | |
| project: { | |
| id: 'test', | |
| name: 'Test', | |
| settings: { | |
| mcpServers: [ | |
| { name: 'test-server', command: 'python', args: ['-m', 'server'] }, | |
| { name: 'another', command: 'npx', env: { KEY: 'val' } }, | |
| ], | |
| }, | |
| notebooks: [], | |
| }, | |
| }) | |
| expect(result.success).toBe(true) | |
| if (result.success) { | |
| expect(result.data.project.settings?.mcpServers).toHaveLength(2) | |
| expect(result.data.project.settings?.mcpServers?.[0]).toMatchObject({ | |
| name: 'test-server', | |
| command: 'python', | |
| args: ['-m', 'server'], | |
| }) | |
| expect(result.data.project.settings?.mcpServers?.[1]).toMatchObject({ | |
| name: 'another', | |
| command: 'npx', | |
| env: { KEY: 'val' }, | |
| }) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/deepnote-file/agent-block-schema.test.ts` around lines 29
- 49, The test currently only asserts the length of project.settings.mcpServers
after parsing with deepnoteFileSchema, which can miss shape regressions; update
the test to assert the parsed MCP entries' fields (for example verify each
entry's name, command, args, and env where provided) against the expected values
so the schema actually preserves command/args/env (use
deepnoteFileSchema.safeParse result.data.project.settings?.mcpServers and assert
entries equal or match the expected objects).
| export const mcpServerSchema = z.object({ | ||
| name: z.string(), | ||
| command: z.string(), | ||
| args: z.array(z.string()).optional(), | ||
| env: z.record(z.string()).optional(), | ||
| }) |
There was a problem hiding this comment.
Reject blank MCP server names and commands.
z.string() still accepts '' and whitespace here, so invalid configs pass schema validation and only fail later when the runtime tries to resolve or spawn the server.
Proposed fix
export const mcpServerSchema = z.object({
- name: z.string(),
- command: z.string(),
+ name: z.string().trim().min(1),
+ command: z.string().trim().min(1),
args: z.array(z.string()).optional(),
env: z.record(z.string()).optional(),
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const mcpServerSchema = z.object({ | |
| name: z.string(), | |
| command: z.string(), | |
| args: z.array(z.string()).optional(), | |
| env: z.record(z.string()).optional(), | |
| }) | |
| export const mcpServerSchema = z.object({ | |
| name: z.string().trim().min(1), | |
| command: z.string().trim().min(1), | |
| args: z.array(z.string()).optional(), | |
| env: z.record(z.string()).optional(), | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts` around lines 253 -
258, The schema mcpServerSchema currently uses z.string() for the name and
command fields which allows empty or whitespace-only values; update those fields
to trim whitespace and enforce at least one character, e.g. replace name:
z.string() and command: z.string() with name: z.string().trim().min(1) and
command: z.string().trim().min(1) so blank or whitespace-only server
names/commands are rejected at validation time.
| describe('Agent blocks', () => { | ||
| it('returns comment-based code for agent block', () => { | ||
| const block: AgentBlock = { | ||
| id: '123', | ||
| type: 'agent', | ||
| content: 'Analyze the data', | ||
| blockGroup: 'abc', | ||
| sortingKey: 'a0', | ||
| metadata: { | ||
| deepnote_agent_model: 'gpt-4o', | ||
| }, | ||
| } | ||
|
|
||
| const result = createPythonCode(block) | ||
|
|
||
| expect(result).toEqual('# [agent block] System prompt:\n# Analyze the data') | ||
| }) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Replace this duplicate happy-path test with a missing edge case.
This is effectively the same assertion as the earlier agent prompt test, so it adds little coverage. A special-characters case would pull more weight here.
Proposed replacement
describe('Agent blocks', () => {
- it('returns comment-based code for agent block', () => {
+ it('creates Python comment for agent block with special characters', () => {
const block: AgentBlock = {
id: '123',
type: 'agent',
- content: 'Analyze the data',
+ content: 'Analyze "sales"\nPath: C:\\tmp\n#keep-comment',
blockGroup: 'abc',
sortingKey: 'a0',
metadata: {
deepnote_agent_model: 'gpt-4o',
},
}
const result = createPythonCode(block)
- expect(result).toEqual('# [agent block] System prompt:\n# Analyze the data')
+ expect(result).toEqual(
+ dedent`
+ # [agent block] System prompt:
+ # Analyze "sales"
+ # Path: C:\\tmp
+ # `#keep-comment`
+ `.trim(),
+ )
})
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| describe('Agent blocks', () => { | |
| it('returns comment-based code for agent block', () => { | |
| const block: AgentBlock = { | |
| id: '123', | |
| type: 'agent', | |
| content: 'Analyze the data', | |
| blockGroup: 'abc', | |
| sortingKey: 'a0', | |
| metadata: { | |
| deepnote_agent_model: 'gpt-4o', | |
| }, | |
| } | |
| const result = createPythonCode(block) | |
| expect(result).toEqual('# [agent block] System prompt:\n# Analyze the data') | |
| }) | |
| describe('Agent blocks', () => { | |
| it('creates Python comment for agent block with special characters', () => { | |
| const block: AgentBlock = { | |
| id: '123', | |
| type: 'agent', | |
| content: 'Analyze "sales"\nPath: C:\\tmp\n#keep-comment', | |
| blockGroup: 'abc', | |
| sortingKey: 'a0', | |
| metadata: { | |
| deepnote_agent_model: 'gpt-4o', | |
| }, | |
| } | |
| const result = createPythonCode(block) | |
| expect(result).toEqual( | |
| dedent` | |
| # [agent block] System prompt: | |
| # Analyze "sales" | |
| # Path: C:\\tmp | |
| # `#keep-comment` | |
| `.trim(), | |
| ) | |
| }) | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/python-code.test.ts` around lines 984 - 1000, Replace the
duplicate happy-path test in the "Agent blocks" suite with an edge-case that
verifies createPythonCode handles special characters; update the it(...) case
that currently builds an AgentBlock (id '123', type 'agent', content 'Analyze
the data', metadata.deepnote_agent_model 'gpt-4o') to instead use an AgentBlock
whose content contains special characters (quotes, newlines, non-ASCII, and
comment-like sequences) and assert createPythonCode returns properly
escaped/commented Python output; locate the test inside the describe('Agent
blocks') block and modify the AgentBlock fixture and expected result to validate
correct handling by createPythonCode.
| export interface AgentBlockContext { | ||
| kernel: KernelClient | ||
| file: DeepnoteFile | ||
| notebookIndex: number | ||
| agentBlockIndex: number | ||
| collectedOutputs: Map<string, { outputs: unknown[]; executionCount: number | null }> | ||
| onLog?: (message: string) => void | ||
| onAgentEvent?: (event: AgentStreamEvent) => void | ||
| integrations?: Array<{ id: string; name: string; type: string }> | ||
| } |
There was a problem hiding this comment.
Resolve relative MCP paths from the project directory.
The examples and fixtures pass roots like ./examples and ./data, but this handler forwards them unchanged and AgentBlockContext has no working-directory field. The same notebook will read and write different locations depending on the caller's shell CWD or --cwd.
Also applies to: 155-167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/runtime-core/src/agent-handler.ts` around lines 12 - 21,
AgentBlockContext currently lacks any working-directory info so relative MCP
roots like "./examples" are forwarded unchanged; add a workingDirectory (string)
field to AgentBlockContext and update the code paths that forward/consume MCP
roots (the handler that forwards roots and the code referenced around the same
area) to resolve relative roots against this workingDirectory using
path.resolve(workingDirectory, root) (leave absolute paths untouched). Ensure
the new workingDirectory is threaded through wherever AgentBlockContext is
constructed so consumers use the resolved absolute paths.
| const newBlock: Extract<DeepnoteBlock, { type: 'code' }> = { | ||
| id: randomUUID().replace(/-/g, ''), | ||
| blockGroup: randomUUID().replace(/-/g, ''), | ||
| sortingKey: generateSortingKey(insertIndex), | ||
| type: 'code', | ||
| content: code, | ||
| metadata: {}, | ||
| executionCount: null, | ||
| outputs: [], | ||
| } |
There was a problem hiding this comment.
Persist execution results on the inserted code block.
You mirror results into collectedOutputs, but newBlock.outputs and newBlock.executionCount stay at their initial empty/null values. Saved notebooks and snapshots then show agent-created code cells as never run.
Suggested fix
try {
const result = await context.kernel.execute(code)
+ newBlock.outputs = result.outputs
+ newBlock.executionCount = result.executionCount
const outputTexts: string[] = []
for (const output of result.outputs) {
const out = output as Record<string, unknown>Also applies to: 205-243
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/runtime-core/src/agent-handler.ts` around lines 190 - 199, The
created code block (newBlock) is never updated with the agent execution results,
so newBlock.outputs and newBlock.executionCount remain empty while results are
stored only in collectedOutputs; update newBlock right after execution by
assigning newBlock.outputs = cloneDeep(collectedOutputs[newBlock.id] ||
collectedOutputs[correspondingId] || executionResult.outputs) and
newBlock.executionCount = executionResult.executionCount ||
(Array.isArray(newBlock.outputs) ? newBlock.outputs.length ?
(newBlock.outputs[0].execution_count ?? null) : null : null); ensure you use the
same identifiers used when mirroring results (collectedOutputs, executionResult)
and deep-copy the outputs before assigning so saved notebooks/snapshots persist
the executed outputs; apply the same fix in the other insertion branch that
mirrors results (the block handling the alternate insert path).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts`:
- Around line 262-265: The metadata schema allows empty or whitespace-only agent
model identifiers because deepnote_agent_model is defined as
z.string().default('auto') without trimming or emptiness checks; update the
executableBlockMetadataSchema extension for deepnote_agent_model to first trim
input and then enforce non-empty content (e.g., transform to s.trim() and refine
or validate that length > 0) so blank strings or strings with only whitespace
are rejected while keeping the default 'auto'.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: b19489a4-a993-45c4-9fff-8ef5f02fc688
📒 Files selected for processing (1)
packages/blocks/src/deepnote-file/deepnote-file-schema.ts
| metadata: executableBlockMetadataSchema | ||
| .extend({ | ||
| deepnote_agent_model: z.string().default('auto'), | ||
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/blocks/src/deepnote-file/deepnote-file-schema.ts | sed -n '260,270p'Repository: deepnote/deepnote
Length of output: 486
🏁 Script executed:
# Find similar validation patterns for model/agent fields in the codebase
rg "z\.string\(\)" packages/blocks/src/deepnote-file/deepnote-file-schema.ts -A 2 -B 1 | head -40Repository: deepnote/deepnote
Length of output: 1508
🏁 Script executed:
# Check mcpServerSchema to see how other fields are validated
rg "mcpServerSchema" packages/blocks/src/deepnote-file/deepnote-file-schema.ts -B 5 -A 5Repository: deepnote/deepnote
Length of output: 1151
🏁 Script executed:
# Search for usage of deepnote_agent_model to see if there's runtime validation
rg "deepnote_agent_model" packages/blocks/src/ -A 3 -B 1Repository: deepnote/deepnote
Length of output: 2470
🏁 Script executed:
# Check if there are similar min(1) validations elsewhere for model/identifier fields
rg "\.min\(1\)" packages/blocks/src/deepnote-file/ -B 2 -A 1Repository: deepnote/deepnote
Length of output: 43
🏁 Script executed:
# Check how deepnote_agent_model is used at runtime
rg "agent_model" packages/blocks/src/ -t ts -t tsx --max-count 20Repository: deepnote/deepnote
Length of output: 88
🏁 Script executed:
# Look for any validation that happens after schema parsing
rg "deepnote_agent_model|agent.*model" packages/blocks/src/python-code.ts -A 5 -B 5Repository: deepnote/deepnote
Length of output: 43
🏁 Script executed:
# Check what other string fields in schemas use .min(1) or similar validation
rg "z\.string\(\).*min|z\.string\(\).*trim" packages/blocks/src/ -B 1 -A 1 | head -30Repository: deepnote/deepnote
Length of output: 43
🏁 Script executed:
# Find any validation or usage of agent model after parsing
rg "agent.*model" packages/blocks/src/ -A 2 -B 2 | head -50Repository: deepnote/deepnote
Length of output: 2429
🏁 Script executed:
# Check the python-code.ts file to see if there's any validation of the model
find packages/blocks/src -name "python-code.ts" -exec cat -n {} \; | head -100Repository: deepnote/deepnote
Length of output: 3649
🏁 Script executed:
# Look for what models are valid and if there's a list/enum somewhere
rg "gpt-4|auto|model" packages/blocks/src/deepnote-file/deepnote-file-schema.ts -B 2 -A 2Repository: deepnote/deepnote
Length of output: 244
🏁 Script executed:
# Find the agent blocks implementation to see how deepnote_agent_model is used
find packages/blocks/src -name "*agent*" -type f | head -10Repository: deepnote/deepnote
Length of output: 103
🏁 Script executed:
# Look at the agent blocks implementation
find packages/blocks/src -name "*agent-blocks*" -type f -exec cat {} \; | head -150Repository: deepnote/deepnote
Length of output: 574
🏁 Script executed:
# Check if there's an enum or validation for valid agent models
rg "auto|gpt-4|model.*enum|model.*union" packages/blocks/src/deepnote-file/ -B 2 -A 2Repository: deepnote/deepnote
Length of output: 534
🏁 Script executed:
# Check if deepnote_agent_model is used elsewhere in validation or type definitions
rg "deepnote_agent_model" packages/blocks/src/ -B 3 -A 3 | grep -v ".test.ts"Repository: deepnote/deepnote
Length of output: 720
🏁 Script executed:
# Verify that empty strings would indeed pass the current schema
python3 << 'EOF'
# Simulate Zod behavior - z.string().default('auto') accepts any string
test_values = ['', ' ', ' ', 'auto', 'gpt-4o', 'invalid-model']
for val in test_values:
# Current schema behavior
print(f"Current schema: '{val}' -> PASS (accepted)")
# Proposed schema behavior with trim and min(1)
trimmed = val.strip()
if len(trimmed) >= 1:
print(f"Proposed schema: '{val}' -> PASS (accepted as '{trimmed}')")
else:
print(f"Proposed schema: '{val}' -> FAIL (rejected)")
print()
EOFRepository: deepnote/deepnote
Length of output: 621
Disallow blank agent model identifiers.
The schema currently accepts empty strings and whitespace via z.string().default('auto'), allowing invalid model values to pass validation. This should be rejected at schema level.
Proposed fix
metadata: executableBlockMetadataSchema
.extend({
- deepnote_agent_model: z.string().default('auto'),
+ deepnote_agent_model: z.string().trim().min(1).default('auto'),
deepnote_mcp_servers: z.array(mcpServerSchema).optional(),
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| metadata: executableBlockMetadataSchema | |
| .extend({ | |
| deepnote_agent_model: z.string().default('auto'), | |
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), | |
| metadata: executableBlockMetadataSchema | |
| .extend({ | |
| deepnote_agent_model: z.string().trim().min(1).default('auto'), | |
| deepnote_mcp_servers: z.array(mcpServerSchema).optional(), |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/deepnote-file/deepnote-file-schema.ts` around lines 262 -
265, The metadata schema allows empty or whitespace-only agent model identifiers
because deepnote_agent_model is defined as z.string().default('auto') without
trimming or emptiness checks; update the executableBlockMetadataSchema extension
for deepnote_agent_model to first trim input and then enforce non-empty content
(e.g., transform to s.trim() and refine or validate that length > 0) so blank
strings or strings with only whitespace are rejected while keeping the default
'auto'.
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/blocks/src/index.ts`:
- Line 2: The schema is missing the AgentBlock and McpServerConfig type
definitions referenced by the new agent exports; update schema.ts to include
type definitions for AgentBlock and McpServerConfig (matching the shapes
documented in blocks-agent.md) by either re-running the project's schema
generation script to regenerate schema.ts or by manually adding the two type
declarations to schema.ts so that the exported symbol is covered; look for the
AgentBlock and McpServerConfig identifiers and ensure their fields match the
documented structure and are exported from schema.ts.
In `@packages/blocks/src/output-text.ts`:
- Around line 28-34: The code assumes every element of out.traceback is a string
and calls .replace on each, which can throw if elements are non-strings; update
the includeTraceback handling in output-text.ts (the block using
options?.includeTraceback and out.traceback) to filter or coerce elements to
strings before calling .replace — e.g., iterate over (out.traceback as
unknown[]), use typeof checks or String(...) to ensure each item is a string (or
skip non-strings), then strip ANSI sequences and join; ensure the logic
preserves existing newline/prefix behavior and only processes elements that are
valid strings.
In `@packages/runtime-core/src/agent-handler.ts`:
- Around line 222-223: The execute handler currently unsafely casts input to {
content: string } and uses mdContent without validation; update the execute
function to validate that input is an object with a string content property
(same checks used in the code block tool), return a clear error or throw if
validation fails, and only assign mdContent when content is confirmed to be a
string to avoid runtime errors.
- Around line 168-169: The execute handler currently unsafely casts input to {
code: string } (execute: async (input: unknown) => { const { code } = input as {
code: string } }), which can crash on malformed tool calls; update execute to
validate input before destructuring by checking input is an object, not null,
has a "code" key, and that typeof code === "string", and handle invalid shapes
by throwing a clear error or returning a controlled failure response; ensure all
subsequent uses of code rely on the validated value rather than the raw cast.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e77d4213-20a1-4261-b40e-45b057d519d2
📒 Files selected for processing (5)
packages/blocks/src/index.tspackages/blocks/src/output-text.tspackages/mcp/src/tools/execution.tspackages/mcp/src/utils.tspackages/runtime-core/src/agent-handler.ts
| @@ -1,8 +1,10 @@ | |||
| export { UnsupportedBlockTypeError } from './blocks' | |||
| export { isAgentBlock } from './blocks/agent-blocks' | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if agent block documentation exists in references
rg -l 'agent' skills/deepnote/references/ 2>/dev/null || echo "No agent references found"
fd 'schema.ts' skills/deepnote/references/ --exec cat {} 2>/dev/null | head -50Repository: deepnote/deepnote
Length of output: 1877
🏁 Script executed:
#!/bin/bash
# Check schema.ts for AgentBlock and McpServerConfig definitions
echo "=== Searching schema.ts for AgentBlock and McpServerConfig ==="
rg -A 5 'AgentBlock|McpServerConfig' skills/deepnote/references/schema.ts
echo -e "\n=== Checking blocks-agent.md content ==="
cat skills/deepnote/references/blocks-agent.md 2>/dev/null | head -100Repository: deepnote/deepnote
Length of output: 3484
Ensure AgentBlock and McpServerConfig types are added to schema.ts.
blocks-agent.md is properly documented. However, schema.ts should include the AgentBlock and McpServerConfig type definitions per coding guidelines. The file is auto-generated, so either run the schema generation script or add these types manually.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/index.ts` at line 2, The schema is missing the AgentBlock
and McpServerConfig type definitions referenced by the new agent exports; update
schema.ts to include type definitions for AgentBlock and McpServerConfig
(matching the shapes documented in blocks-agent.md) by either re-running the
project's schema generation script to regenerate schema.ts or by manually adding
the two type declarations to schema.ts so that the exported symbol is covered;
look for the AgentBlock and McpServerConfig identifiers and ensure their fields
match the documented structure and are exported from schema.ts.
| if (options?.includeTraceback && Array.isArray(out.traceback)) { | ||
| text += | ||
| '\n' + | ||
| (out.traceback as string[]) | ||
| // biome-ignore lint/suspicious/noControlCharactersInRegex: strip ANSI escape sequences from traceback | ||
| .map(line => line.replace(/\x1b\[[0-9;]*m/g, '')) | ||
| .join('\n') |
There was a problem hiding this comment.
Traceback elements assumed to be strings without validation.
Array.isArray(out.traceback) doesn't guarantee string elements. If an element isn't a string, .replace() throws.
🛡️ Proposed fix
if (options?.includeTraceback && Array.isArray(out.traceback)) {
text +=
'\n' +
- (out.traceback as string[])
+ out.traceback
// biome-ignore lint/suspicious/noControlCharactersInRegex: strip ANSI escape sequences from traceback
- .map(line => line.replace(/\x1b\[[0-9;]*m/g, ''))
+ .map(line => (typeof line === 'string' ? line.replace(/\x1b\[[0-9;]*m/g, '') : String(line)))
.join('\n')
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (options?.includeTraceback && Array.isArray(out.traceback)) { | |
| text += | |
| '\n' + | |
| (out.traceback as string[]) | |
| // biome-ignore lint/suspicious/noControlCharactersInRegex: strip ANSI escape sequences from traceback | |
| .map(line => line.replace(/\x1b\[[0-9;]*m/g, '')) | |
| .join('\n') | |
| if (options?.includeTraceback && Array.isArray(out.traceback)) { | |
| text += | |
| '\n' + | |
| out.traceback | |
| // biome-ignore lint/suspicious/noControlCharactersInRegex: strip ANSI escape sequences from traceback | |
| .map(line => (typeof line === 'string' ? line.replace(/\x1b\[[0-9;]*m/g, '') : String(line))) | |
| .join('\n') |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/blocks/src/output-text.ts` around lines 28 - 34, The code assumes
every element of out.traceback is a string and calls .replace on each, which can
throw if elements are non-strings; update the includeTraceback handling in
output-text.ts (the block using options?.includeTraceback and out.traceback) to
filter or coerce elements to strings before calling .replace — e.g., iterate
over (out.traceback as unknown[]), use typeof checks or String(...) to ensure
each item is a string (or skip non-strings), then strip ANSI sequences and join;
ensure the logic preserves existing newline/prefix behavior and only processes
elements that are valid strings.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (10)
packages/cli/src/commands/run.ts (3)
248-249:⚠️ Potential issue | 🟡 MinorOrder issue: absolutePath computed before workingDirectory.
Line 248 uses
process.cwd()butworkingDirectoryisn't set until line 249. Should swap order.Fix
- absolutePath = join(process.cwd(), 'prompt.deepnote') workingDirectory = options.cwd ?? process.cwd() + absolutePath = join(workingDirectory, 'prompt.deepnote')🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/run.ts` around lines 248 - 249, The code computes absolutePath using process.cwd() before resolving workingDirectory from options.cwd; swap the two statements so workingDirectory = options.cwd ?? process.cwd() is set first, then compute absolutePath = join(workingDirectory, 'prompt.deepnote') to ensure absolutePath uses the intended workingDirectory value (refer to the variables absolutePath and workingDirectory and the use of options.cwd and join).
472-473:⚠️ Potential issue | 🟠 MajorRedact prompt in debug logs.
safeOptionsmaskstokenbut exposesprompt, which may contain sensitive data.Fix
-const safeOptions = { ...options, token: options.token ? '[redacted]' : undefined } +const safeOptions = { + ...options, + token: options.token ? '[redacted]' : undefined, + prompt: options.prompt ? '[redacted]' : undefined, +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/run.ts` around lines 472 - 473, The debug log currently redacts only options.token but leaves options.prompt exposed; update the creation of safeOptions (used before debug in run.ts) to also mask any prompt field (e.g., set prompt: options.prompt ? '[redacted]' : undefined) so JSON.stringify(safeOptions) will not include sensitive prompt text; ensure you reference and modify the same symbols (options -> safeOptions and the debug(...) call) so the debug output never prints the raw prompt.
272-279:⚠️ Potential issue | 🟠 MajorPrompt appends to last notebook, ignoring
--notebook.When
--notebookis specified, the agent block should go there instead.Fix
if (path && options.prompt) { - const lastNotebook = file.project.notebooks[file.project.notebooks.length - 1] - if (lastNotebook) { - lastNotebook.blocks.push(createAgentBlock(options.prompt, lastNotebook.blocks.length)) + const targetNotebook = options.notebook + ? file.project.notebooks.find(n => n.name === options.notebook) + : file.project.notebooks[file.project.notebooks.length - 1] + + if (targetNotebook) { + targetNotebook.blocks.push(createAgentBlock(options.prompt, targetNotebook.blocks.length)) + } else if (options.notebook) { + throw new Error(`Cannot append prompt: notebook "${options.notebook}" not found`) } else { throw new Error('Cannot append prompt: file contains no notebooks') } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/src/commands/run.ts` around lines 272 - 279, The code always appends the agent block to the last notebook (`lastNotebook`) when `options.prompt` is set, ignoring a specified `--notebook`; update the logic in the block that calls `createAgentBlock` to honor `options.notebook` by locating the target notebook (by name or index, according to how `options.notebook` is represented) in `file.project.notebooks` and appending to that notebook's `blocks` instead of `lastNotebook`; if the requested notebook cannot be found, throw a clear error (replace the current 'Cannot append prompt: file contains no notebooks' behavior) and keep using `createAgentBlock(options.prompt, targetNotebook.blocks.length)` to compute the block index.examples/demo-agent-block.deepnote (2)
80-80:⚠️ Potential issue | 🟠 MajorPath double-nested.
MCP root is
./examples, soexamples/report.mdresolves to./examples/examples/report.md. Usereport.md.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/demo-agent-block.deepnote` at line 80, The step text in examples/demo-agent-block.deepnote incorrectly uses a double-nested path "examples/report.md" (MCP root is ./examples), so change that string to "report.md" so the filesystem MCP tool saves to ./examples/report.md rather than ./examples/examples/report.md; locate and update the line containing "Use the filesystem MCP tool to save a final executive report as `examples/report.md`" to use `report.md` instead.
91-94:⚠️ Potential issue | 🔴 CriticalPin MCP server version.
Unpinned package risks pulling vulnerable versions.
Fix
args: - '-y' - - '@modelcontextprotocol/server-filesystem' + - '@modelcontextprotocol/[email protected]' - ./examples🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/demo-agent-block.deepnote` around lines 91 - 94, The MCP server package reference '@modelcontextprotocol/server-filesystem' is unpinned in the args list; change the argument to a pinned version string (e.g. '@modelcontextprotocol/server-filesystem@<semver>' or exact commit/hash) so the args entry becomes the package name with a specific version; update the occurrence of '@modelcontextprotocol/server-filesystem' in the args array to the chosen pinned version.packages/runtime-core/src/agent-handler.ts (4)
150-160:⚠️ Potential issue | 🟠 MajorRelative MCP paths not resolved.
Paths like
./dataare forwarded unchanged. Behavior depends on shell CWD rather than project directory.
AgentBlockContextneeds aworkingDirectoryfield to resolve relative paths.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` around lines 150 - 160, The MCP paths are not being resolved against the agent/project directory: add a workingDirectory string field to AgentBlockContext and update the code that builds mergedMcpConfig to resolve any relative paths (in s.command, s.args entries that are paths, and s.env values) against context.workingDirectory before creating clients; then pass those resolved values into createMCPClient/Experimental_StdioMCPTransport (keep using resolveEnvVars but feed it the resolved env) so MCP launches use the intended project directory rather than the process CWD.
218-218:⚠️ Potential issue | 🟡 MinorSame validation needed for content.
Fix
- execute: async ({ content: mdContent }) => { + execute: async (input) => { + const mdContent = typeof input === 'object' && input && 'content' in input && typeof input.content === 'string' + ? input.content + : '' + if (!mdContent) return 'Error: content parameter is required' context.onLog?.(' [agent] Adding markdown block')🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` at line 218, The execute handler currently accepts ({ content: mdContent }) without validating mdContent; add the same validation used elsewhere to ensure mdContent is a non-empty string (or the expected type) before proceeding, and handle invalid input by returning/throwing the same error/response pattern used in this module; update the execute function parameter handling and early-return logic for mdContent to match the other validated handlers in this file.
175-184:⚠️ Potential issue | 🟠 MajorPersist outputs on inserted code block.
newBlock.outputsandnewBlock.executionCountstay empty. Saved notebooks show agent-created cells as never run.Fix
try { const result = await context.kernel.execute(code) + newBlock.outputs = result.outputs + newBlock.executionCount = result.executionCount blockOutputs.push({Also applies to: 190-202
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` around lines 175 - 184, When creating the new code block in agent-handler (the newBlock object), populate newBlock.outputs and newBlock.executionCount from the code-execution result instead of leaving them empty so saved notebooks reflect that the cell has run; locate the two newBlock constructions (the one shown and the duplicate around lines 190-202), take the execution result object produced by the executor (e.g., the variable that contains stdout/stderr/outputs and count) and assign its outputs to newBlock.outputs and its execution count (or a derived number) to newBlock.executionCount before persisting the notebook.
172-172:⚠️ Potential issue | 🟡 MinorValidate tool input.
{ code }destructuring assumes correct shape. Malformed calls cause runtime errors.Fix
- execute: async ({ code }) => { + execute: async (input) => { + const code = typeof input === 'object' && input && 'code' in input && typeof input.code === 'string' + ? input.code + : '' + if (!code) return 'Error: code parameter is required' context.onLog?.(' [agent] Adding code block and executing...')🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/runtime-core/src/agent-handler.ts` at line 172, The execute handler currently destructures ({ code }) without validation; add an explicit guard at the start of the execute: async ({ code }) => { ... } function to verify the incoming payload exists and that code is a string (e.g., if (!payload || typeof code !== 'string') { /* log and return/throw a structured error */ }), and ensure you return or throw a clear, consistent error response instead of letting a runtime exception bubble up; update any callers or tests expecting error shapes if needed.packages/cli/README.md (1)
184-189:⚠️ Potential issue | 🟡 MinorInline secrets in examples.
Showing
OPENAI_API_KEY=sk-...inline normalizes pasting keys into shell history. Preferexportonce.Fix
-# Run an agent with a prompt (appends an agent block to the file) -OPENAI_API_KEY=sk-... deepnote run my-project.deepnote --prompt "Analyze the sales data" +# Run an agent with a prompt (appends an agent block to the file) +# First: export OPENAI_API_KEY="sk-..." +deepnote run my-project.deepnote --prompt "Analyze the sales data" -# Run an agent block standalone (no file needed) -OPENAI_API_KEY=sk-... deepnote run --prompt "Write a hello world script" +# Run an agent block standalone (no file needed) +deepnote run --prompt "Write a hello world script"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/cli/README.md` around lines 184 - 189, The README examples include inline secret assignment like `OPENAI_API_KEY=sk-... deepnote run ...`; update both examples (the "Run an agent with a prompt" and "Run an agent block standalone" code blocks) to avoid inline secrets by showing a safer pattern: instruct users to set the key once with `export OPENAI_API_KEY=...` (or reference using an env file like `--env-file .env`) and replace the inline `OPENAI_API_KEY=sk-...` occurrences with a note telling users to export the variable beforehand or use an env-file, keeping the example commands free of literal keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@skills/deepnote/references/blocks-agent.md`:
- Around line 58-64: Replace the inline secret usage in the bash examples so the
API key is exported separately instead of prefixed to the command; update the
two examples that currently show "OPENAI_API_KEY=sk-... deepnote run ..." and
"OPENAI_API_KEY=sk-... deepnote run my-project.deepnote ..." to demonstrate
setting the secret with an export (e.g., export OPENAI_API_KEY=...) on its own
line, then running the corresponding deepnote run commands, and keep a note that
users should unset or use a secure secret mechanism afterwards.
In `@test-fixtures/agent-block.deepnote`:
- Around line 8-14: The MCP server package is unpinned in the mcpServers fixture
(entry with name: filesystem and args containing
"@modelcontextprotocol/server-filesystem"); change that argument to a pinned
release (e.g. "@modelcontextprotocol/server-filesystem@<safe-version>") using
the vetted version from your lockfile or security advisory, so the fixture
explicitly references the approved semver (update any test fixtures or docs that
reference mcpServers/name: filesystem accordingly).
---
Duplicate comments:
In `@examples/demo-agent-block.deepnote`:
- Line 80: The step text in examples/demo-agent-block.deepnote incorrectly uses
a double-nested path "examples/report.md" (MCP root is ./examples), so change
that string to "report.md" so the filesystem MCP tool saves to
./examples/report.md rather than ./examples/examples/report.md; locate and
update the line containing "Use the filesystem MCP tool to save a final
executive report as `examples/report.md`" to use `report.md` instead.
- Around line 91-94: The MCP server package reference
'@modelcontextprotocol/server-filesystem' is unpinned in the args list; change
the argument to a pinned version string (e.g.
'@modelcontextprotocol/server-filesystem@<semver>' or exact commit/hash) so the
args entry becomes the package name with a specific version; update the
occurrence of '@modelcontextprotocol/server-filesystem' in the args array to the
chosen pinned version.
In `@packages/cli/README.md`:
- Around line 184-189: The README examples include inline secret assignment like
`OPENAI_API_KEY=sk-... deepnote run ...`; update both examples (the "Run an
agent with a prompt" and "Run an agent block standalone" code blocks) to avoid
inline secrets by showing a safer pattern: instruct users to set the key once
with `export OPENAI_API_KEY=...` (or reference using an env file like
`--env-file .env`) and replace the inline `OPENAI_API_KEY=sk-...` occurrences
with a note telling users to export the variable beforehand or use an env-file,
keeping the example commands free of literal keys.
In `@packages/cli/src/commands/run.ts`:
- Around line 248-249: The code computes absolutePath using process.cwd() before
resolving workingDirectory from options.cwd; swap the two statements so
workingDirectory = options.cwd ?? process.cwd() is set first, then compute
absolutePath = join(workingDirectory, 'prompt.deepnote') to ensure absolutePath
uses the intended workingDirectory value (refer to the variables absolutePath
and workingDirectory and the use of options.cwd and join).
- Around line 472-473: The debug log currently redacts only options.token but
leaves options.prompt exposed; update the creation of safeOptions (used before
debug in run.ts) to also mask any prompt field (e.g., set prompt: options.prompt
? '[redacted]' : undefined) so JSON.stringify(safeOptions) will not include
sensitive prompt text; ensure you reference and modify the same symbols (options
-> safeOptions and the debug(...) call) so the debug output never prints the raw
prompt.
- Around line 272-279: The code always appends the agent block to the last
notebook (`lastNotebook`) when `options.prompt` is set, ignoring a specified
`--notebook`; update the logic in the block that calls `createAgentBlock` to
honor `options.notebook` by locating the target notebook (by name or index,
according to how `options.notebook` is represented) in `file.project.notebooks`
and appending to that notebook's `blocks` instead of `lastNotebook`; if the
requested notebook cannot be found, throw a clear error (replace the current
'Cannot append prompt: file contains no notebooks' behavior) and keep using
`createAgentBlock(options.prompt, targetNotebook.blocks.length)` to compute the
block index.
In `@packages/runtime-core/src/agent-handler.ts`:
- Around line 150-160: The MCP paths are not being resolved against the
agent/project directory: add a workingDirectory string field to
AgentBlockContext and update the code that builds mergedMcpConfig to resolve any
relative paths (in s.command, s.args entries that are paths, and s.env values)
against context.workingDirectory before creating clients; then pass those
resolved values into createMCPClient/Experimental_StdioMCPTransport (keep using
resolveEnvVars but feed it the resolved env) so MCP launches use the intended
project directory rather than the process CWD.
- Line 218: The execute handler currently accepts ({ content: mdContent })
without validating mdContent; add the same validation used elsewhere to ensure
mdContent is a non-empty string (or the expected type) before proceeding, and
handle invalid input by returning/throwing the same error/response pattern used
in this module; update the execute function parameter handling and early-return
logic for mdContent to match the other validated handlers in this file.
- Around line 175-184: When creating the new code block in agent-handler (the
newBlock object), populate newBlock.outputs and newBlock.executionCount from the
code-execution result instead of leaving them empty so saved notebooks reflect
that the cell has run; locate the two newBlock constructions (the one shown and
the duplicate around lines 190-202), take the execution result object produced
by the executor (e.g., the variable that contains stdout/stderr/outputs and
count) and assign its outputs to newBlock.outputs and its execution count (or a
derived number) to newBlock.executionCount before persisting the notebook.
- Line 172: The execute handler currently destructures ({ code }) without
validation; add an explicit guard at the start of the execute: async ({ code })
=> { ... } function to verify the incoming payload exists and that code is a
string (e.g., if (!payload || typeof code !== 'string') { /* log and
return/throw a structured error */ }), and ensure you return or throw a clear,
consistent error response instead of letting a runtime exception bubble up;
update any callers or tests expecting error shapes if needed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a574191d-b8d7-4e41-9016-6749d491af52
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
examples/demo-agent-block.deepnotepackages/blocks/src/python-code.test.tspackages/cli/README.mdpackages/cli/src/commands/run.tspackages/runtime-core/package.jsonpackages/runtime-core/src/agent-handler.tsskills/deepnote/references/blocks-agent.mdtest-fixtures/agent-block.deepnote
| ```bash | ||
| # Standalone (creates an in-memory notebook with just the agent block) | ||
| OPENAI_API_KEY=sk-... deepnote run --prompt "Write a hello world script" | ||
|
|
||
| # Appended to an existing notebook (runs all blocks, then the agent) | ||
| OPENAI_API_KEY=sk-... deepnote run my-project.deepnote --prompt "Analyze the data" | ||
| ``` |
There was a problem hiding this comment.
Inline secrets in docs.
Same as README—prefer showing export OPENAI_API_KEY=... separately.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@skills/deepnote/references/blocks-agent.md` around lines 58 - 64, Replace the
inline secret usage in the bash examples so the API key is exported separately
instead of prefixed to the command; update the two examples that currently show
"OPENAI_API_KEY=sk-... deepnote run ..." and "OPENAI_API_KEY=sk-... deepnote run
my-project.deepnote ..." to demonstrate setting the secret with an export (e.g.,
export OPENAI_API_KEY=...) on its own line, then running the corresponding
deepnote run commands, and keep a note that users should unset or use a secure
secret mechanism afterwards.
| mcpServers: | ||
| - name: filesystem | ||
| command: npx | ||
| args: | ||
| - "-y" | ||
| - "@modelcontextprotocol/server-filesystem" | ||
| - "./data" |
There was a problem hiding this comment.
Pin MCP server package version.
Unpinned @modelcontextprotocol/server-filesystem may pull vulnerable versions. Pin to a known safe release.
Fix
args:
- "-y"
- - "@modelcontextprotocol/server-filesystem"
+ - "@modelcontextprotocol/[email protected]"
- "./data"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mcpServers: | |
| - name: filesystem | |
| command: npx | |
| args: | |
| - "-y" | |
| - "@modelcontextprotocol/server-filesystem" | |
| - "./data" | |
| mcpServers: | |
| - name: filesystem | |
| command: npx | |
| args: | |
| - "-y" | |
| - "@modelcontextprotocol/server-filesystem@2026.1.14" | |
| - "./data" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test-fixtures/agent-block.deepnote` around lines 8 - 14, The MCP server
package is unpinned in the mcpServers fixture (entry with name: filesystem and
args containing "@modelcontextprotocol/server-filesystem"); change that argument
to a pinned release (e.g.
"@modelcontextprotocol/server-filesystem@<safe-version>") using the vetted
version from your lockfile or security advisory, so the fixture explicitly
references the approved semver (update any test fixtures or docs that reference
mcpServers/name: filesystem accordingly).
|
Closing in favor of #341 |
Summary by CodeRabbit
New Features
Documentation
Tests
Chores