Add positron.runtime.getConsoleHistory extension API - #15207
Merged
Conversation
Adds a read-only Positron Assistant tool that returns recent console executions (input, output, and any error) for a session, letting the model inspect what has already run without executing code. Because it carries no requires-actions tag, it stays available in Ask mode while executeCode remains gated to agent mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
E2E Tests 🚀 Why these tags?
More on automatic tags from changed files. |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Exposes recent console history to extensions via a new positron.ai.getConsoleContent(sessionId?) function, so Posit Assistant can read console content through a native tool rather than the vscode.lm tool allowlist (the pattern the assistant is moving away from). The reading/projection logic is extracted into a shared helper, projectExecutionEntriesToConsoleContent, in the execution history service and reused by both the existing core getConsoleContent tool (for Copilot Chat) and the new extension API (for the native Posit Assistant tool), so the two stay in sync. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The core getConsoleContent tool was exposed to chat clients via vscode.lm. Now that Posit Assistant reads console history natively through the positron.ai.getConsoleContent extension API, this tool is vestigial and contradicts the direction of moving off vscode.lm tools, so remove it (tool data, class, registration, and tool-name enum entry). The projection logic (projectExecutionEntriesToConsoleContent) stays in the execution history service, still used by the extension API, and gains a direct unit test in place of the removed tool's tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate getConsoleContent from positron.ai to positron.runtime, where it sits alongside the other session-scoped read APIs getSessionVariables and querySessionTables (which back the equivalent inspect tools). Reading a session's console history is a runtime-state concern, not an AI-specific one, so it belongs with the runtime session APIs rather than the AI feature surface. The signature now takes a required sessionId to match its siblings; the caller resolves the foreground session. Plumbing moves from the AiFeatures extHost/mainThread pair to the LanguageRuntime pair accordingly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add an optional numberOfEntries argument to positron.runtime.getConsoleContent, applied server-side (in the shared projection helper) so only the requested number of most recent entries crosses the extension-host boundary instead of the whole session history. Defaults to 5; non-positive values fall back to the default. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a "Zed: Get Console Content" command to the positron-zed extension that reads the foreground session's recent console history via positron.runtime.getConsoleContent (prompting for an optional entry count) and opens the result in a JSON editor. Provides a way to exercise the new API without Posit Assistant installed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
jonvanausdeln
left a comment
Contributor
There was a problem hiding this comment.
2 things/questions:
- Finding from our good friend Claude
### `getConsoleContent` with an unknown `sessionId` allocates a permanent `SessionExecutionHistory`
`$getConsoleContent` reads through `IExecutionHistoryService.getExecutionEntries(sessionId)`, which
doesn't throw for a session it hasn't seen — it *creates* one
(`executionHistory.ts:335-352`):
- constructs a `SessionExecutionHistory`
- `this._register(history)`s it onto the long-lived service
- stores it in `_executionHistories` and returns empty entries
Each of those registers an `onWillSaveState` listener that lives for the workbench lifetime
(`sessionExecutionHistory.ts:62`), and nothing ever releases it. Since `numberOfEntries` and
`sessionId` now come straight from extension input, `getConsoleContent(someRandomId)` called
repeatedly grows both memory and the save-state listener list without bound.
Two smaller consequences of the same behavior:
- A caller passing a stale or wrong session ID gets `[]` and can't tell "this session has no
history" from "this session doesn't exist."
- It's inconsistent with the sibling APIs this one is modeled on. `getSessionVariables` /
`querySessionTables` go through `getVariablesClient`, which throws
`No variables provider found for session ${sessionId}` (`sessionVariableQueries.ts:21`).
Worth noting the create-on-read is arguably a latent bug in `getExecutionEntries` itself —
`clearExecutionEntries`, immediately below it, *does* throw for an unknown session ID. This PR is
just the first caller to hand it untrusted input.
**Two ways to fix, either works:**
1. Validate in `$getConsoleContent` before reading, and throw in the siblings' style so the error
surface is consistent across the session-scoped read APIs.
2. Give `IExecutionHistoryService` a non-allocating read path (e.g. have `getExecutionEntries`
return `[]` for unknown sessions instead of creating, or add a separate read-only accessor).
This fixes it for any future caller too, at the cost of touching shared service behavior.
I'd lean toward (1) for this PR since it keeps the blast radius inside the new API, with (2) as a
follow-up if you want the service itself hardened.
One piece of good news: `save()` early-returns when `!_dirty`, so bogus session IDs don't write
empty history keys into workspace storage. The cost is memory and listeners, not persisted junk.
Worth a test for this too — the helper tests are solid, but nothing currently covers
`$getConsoleContent`, which is where this lives.```
2. Since things like secrets/api-keys/etc could be exposed in the console, do we want to have any gating? Or just leave that up to the extension (assistant in this case) ?
$getConsoleContent passed untrusted sessionId straight into getExecutionEntries(), which creates-on-read: an unknown ID would allocate and permanently register a SessionExecutionHistory (with a workbench-lifetime onWillSaveState listener), growing memory and the listener list without bound, and returning [] instead of erroring. Validate the session up front via a new sessionConsoleContent helper (mirroring sessionVariableQueries), throwing "No such session" for unknown IDs so the create-on-read path is never reached and the error surface matches the sibling session-scoped read APIs. Add coverage for the known-session and unknown-session (no-allocation) paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…output-retrieval # Conflicts: # extensions/positron-zed/package.json
Rename getConsoleContent to getConsoleHistory across the API surface, protocol, and helpers, and add a console.historyApiEnabled setting (default true) that gates extension access to console history. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Thanks! I redid some of the session-getting logic. I also renamed the API calls to For security: I did consider this previously, and just added a default-on setting that users can disable this API with. Disabling the setting will throw an error if the API is called. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a read-only
positron.runtime.getConsoleHistory(sessionId, numberOfEntries?)extension API that returns recent console history for a session: the code fragments that have already run, each paired with its output and any error. It sits alongside the other session-scoped read APIsgetSessionVariablesandquerySessionTables, and reads from the execution history service via a sharedprojectExecutionEntriesToConsoleHistoryhelper (completed executions only, oldest first, skipping the startup banner and entries without input).numberOfEntrieslimits the result to the most recent N and defaults to 5, applied server-side so only those entries cross the extension-host boundary rather than the whole session history.Console history reading is gated by a new
console.historyApiEnabledsetting (WINDOW-scoped, default true). Users can disable it for privacy; when disabled the API rejects rather than returning content. The setting is read live so a mid-session toggle takes effect immediately.This exists so Posit Assistant can read console history through a native tool, in line with the direction of moving assistant tools off the
vscode.lmsurface. The companion native tool lives in the assistant repo on branchsamclark2015/expose-getconsolecontent-tool(posit-dev/assistant#1924).Part of posit-dev/assistant#1826 (split code execution and console context into separate tools).
History of this PR
This PR originally added a core
getConsoleContenttool exposed viavscode.lm. That tool has since been removed in favor of the native extension API above, so the net change to Positron core is the newpositron.runtimeAPI (plus its shared projection helper and privacy setting), not avscode.lmtool. Recommend squashing on merge.Release Notes
New Features
Bug Fixes
Validation Steps
@:assistant
Without Posit Assistant (via the
positron-zeddemo extension):With Posit Assistant (native
getConsoleHistorytool, posit-dev/assistant#1924):positron.runtime.getConsoleHistoryand answers without re-running any code.