Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/ipynb/src/ipynbMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No
// --- Start Positron ---
context.subscriptions.push(vscode.commands.registerCommand('ipynb.newUntitledIpynb', async (languageId?: string) => {
// Try to use Positron's foreground session's language, fall back to 'plaintext'.
const language = languageId ?? (await positron.runtime.getForegroundSession())?.runtimeMetadata?.languageId ?? 'plaintext';
const language = languageId ?? (await positron.runtime.getForegroundSession())?.runtimeMetadata.languageId ?? 'plaintext';
const cell = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, '', language);
const data = new vscode.NotebookData([cell]);
data.metadata = {
Expand Down
5 changes: 4 additions & 1 deletion extensions/positron-assistant/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -590,5 +590,8 @@
"@anthropic-ai/sdk": "^0.57.0",
"@github/copilot-language-server": "^1.335.0",
"vscode-languageclient": "^9.0.1"
}
},
"extensionDependencies": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Assistant really does have to run in the same process, but the Jupyter extension doesn't? Do you know why? (Just for my own learning)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question... it's because Assistant runs code with execution observers, which are one of the things that hasn't been factored to work across different extension hosts.

"positron.positron-supervisor"
]
}
8 changes: 1 addition & 7 deletions extensions/positron-assistant/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,13 +315,7 @@ export function registerAssistantTools(
]);
}

let session: positron.LanguageRuntimeSession | undefined;
const sessions = await positron.runtime.getActiveSessions();
if (sessions && sessions.length > 0) {
session = sessions.find(
(session) => session.metadata.sessionId === options.input.sessionIdentifier,
);
}
const session = await positron.runtime.getSession(options.input.sessionIdentifier);
if (!session) {
return new vscode.LanguageModelToolResult([
new vscode.LanguageModelTextPart('[[]]')
Expand Down
3 changes: 3 additions & 0 deletions extensions/positron-connections/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@
]
}
},
"extensionDependencies": [
"positron.positron-supervisor"
],
"devDependencies": {
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
Expand Down
4 changes: 4 additions & 0 deletions extensions/positron-javascript/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ export class JavaScriptLanguageRuntimeSession implements positron.LanguageRuntim
return Promise.resolve(positron.RuntimeCodeFragmentStatus.Complete);
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

createClient(id: string, type: positron.RuntimeClientType, params: any): Thenable<void> {
if (type === positron.RuntimeClientType.Variables) {
// The only client type we support right now is an environment.
Expand Down
10 changes: 2 additions & 8 deletions extensions/positron-python/src/client/llm-tools/llm-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@ export function registerPythonLanguageModelTools(
'getAttachedPythonPackages',
{
invoke: async (options, _token) => {
let session: positron.LanguageRuntimeSession | undefined;
let session: positron.BaseLanguageRuntimeSession | undefined;
if (options.input.sessionIdentifier) {
const sessions = await positron.runtime.getActiveSessions();
if (sessions && sessions.length > 0) {
session = sessions.find(
(session) => session.metadata.sessionId === options.input.sessionIdentifier,
);
}

session = await positron.runtime.getSession(options.input.sessionIdentifier);
if (!session) {
session = await positron.runtime.getForegroundSession();
}
Expand Down
4 changes: 3 additions & 1 deletion extensions/positron-python/src/client/positron/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,9 @@ export class PythonRuntimeManager implements IPythonRuntimeManager, Disposable {
if (sessionsToShutdown.length > 0) {
traceInfo(`Shutting down ${sessionsToShutdown.length} sessions using Python runtime at ${pythonPath}`);
await Promise.all(
sessionsToShutdown.map((session) => session.shutdown(positron.RuntimeExitReason.Shutdown)),
sessionsToShutdown.map(async (session) => {
session.shutdown(positron.RuntimeExitReason.Shutdown);
}),
);
// Remove the runtime from our registry so we can recreate it
this.registeredPythonRuntimes.delete(pythonPath);
Expand Down
6 changes: 5 additions & 1 deletion extensions/positron-python/src/client/positron/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export class PythonRuntimeSession implements positron.LanguageRuntimeSession, vs
/** Information about the runtime that is only available after starting */
private _runtimeInfo?: positron.LanguageRuntimeInfo;

dynState: positron.LanguageRuntimeDynState;
private dynState: positron.LanguageRuntimeDynState;

onDidReceiveRuntimeMessage = this._messageEmitter.event;

Expand Down Expand Up @@ -168,6 +168,10 @@ export class PythonRuntimeSession implements positron.LanguageRuntimeSession, vs
return this._runtimeInfo;
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

async debug(request: positron.DebugProtocolRequest): Promise<positron.DebugProtocolResponse> {
if (this._kernel) {
return await this._kernel.debug(request);
Expand Down
4 changes: 4 additions & 0 deletions extensions/positron-r/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ export class RSession implements positron.LanguageRuntimeSession, vscode.Disposa
return this._kernel?.runtimeInfo;
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

/**
* Opens a resource in the runtime.
* @param resource The resource to open.
Expand Down
22 changes: 15 additions & 7 deletions extensions/positron-reticulate/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,13 @@ export class ReticulateRuntimeManager implements positron.LanguageRuntimeManager
// We have a free R session, we can attach to it. First we need to figure out if there's
// a prefered one.
// TODO: maybe show a quick menu so the user can select the session they want to attach to?
return freeRSessions[0];
const session = freeRSessions[0];

// Resolve the session to a full session object. Note that this
// typecast is only safe because we ensure that this extension
// runs on the same extension host as the Python and R
// extensions, via declared dependencies in package.json.
return session as positron.LanguageRuntimeSession;
} else {
progress.report({ increment: 2, message: vscode.l10n.t('Starting a new R session') });
// We need to create a new R session.
Expand Down Expand Up @@ -297,8 +303,10 @@ export class ReticulateRuntimeManager implements positron.LanguageRuntimeManager
progress.report({ increment: 10, message: vscode.l10n.t('Finding the host R session') });
const rSession = await (async () => {
for (let attempt = 1; attempt <= 5; attempt++) {
const sessions = await positron.runtime.getActiveSessions();
const hostRSession = sessions.find((sess) => sess.metadata.sessionId === hostRSessionId);
// Note that this typecast is only safe because we ensure that
// this extension runs on the same extension host as the Python
// and R extensions, via declared dependencies in package.json.
const hostRSession = await positron.runtime.getSession(hostRSessionId) as positron.LanguageRuntimeSession | undefined;
if (hostRSession) {
return hostRSession;
}
Expand Down Expand Up @@ -652,6 +660,10 @@ class ReticulateRuntimeSession implements positron.LanguageRuntimeSession {
);
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return this.pythonSession.getDynState();
}

createPythonRuntimeSession(runtimeMetadata: positron.LanguageRuntimeMetadata, sessionMetadata: positron.RuntimeSessionMetadata, kernelSpec?: JupyterKernelSpec): positron.LanguageRuntimeSession {
const api = vscode.extensions.getExtension('ms-python.python');
if (!api) {
Expand Down Expand Up @@ -746,10 +758,6 @@ class ReticulateRuntimeSession implements positron.LanguageRuntimeSession {
return this.pythonSession.runtimeMetadata;
}

public get dynState() {
return this.pythonSession.dynState;
}

public get runtimeInfo() {
return this.pythonSession.runtimeInfo;
}
Expand Down
6 changes: 5 additions & 1 deletion extensions/positron-runtime-debugger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,9 @@
},
"devDependencies": {
"@types/node": "^24.1.0"
}
},
"extensionDependencies": [
"ms-python.python",
"positron.positron-r"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,8 @@ function getActiveNotebookCell(): vscode.NotebookCell | undefined {

/** Get the language runtime session for a notebook. */
export async function getNotebookSession(notebookUri: vscode.Uri): Promise<positron.LanguageRuntimeSession | undefined> {
const runtimeSessions = await positron.runtime.getActiveSessions();
const runtimeSession = runtimeSessions.find(
(session) => session.metadata.notebookUri && isUriEqual(session.metadata.notebookUri, notebookUri)
);
// This cast is safe only because our package.json ensures that this
// extension runs in the same extension host as the notebook kernels.
const runtimeSession = await positron.runtime.getNotebookSession(notebookUri) as positron.LanguageRuntimeSession | undefined;
return runtimeSession;
}
5 changes: 3 additions & 2 deletions extensions/positron-supervisor/src/KallichoreAdapterApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1319,14 +1319,15 @@ export class KCApi implements PositronSupervisorApi {

// Find the session in our list
const kallichoreSession = this._sessions.find(s => s.metadata.sessionId === session.metadata.sessionId);
const sessionName = (await session.getDynState()).sessionName;
if (!kallichoreSession) {
vscode.window.showInformationMessage(vscode.l10n.t('Active session {0} not managed by the kernel supervisor', session.dynState.sessionName));
vscode.window.showInformationMessage(vscode.l10n.t('Active session {0} not managed by the kernel supervisor', sessionName));
return;
}

// Ensure the session is still active
if (kallichoreSession.runtimeState === positron.RuntimeState.Exited) {
vscode.window.showInformationMessage(vscode.l10n.t('Session {0} is not running', session.dynState.sessionName));
vscode.window.showInformationMessage(vscode.l10n.t('Session {0} is not running', sessionName));
return;
}

Expand Down
4 changes: 4 additions & 0 deletions extensions/positron-supervisor/src/KallichoreSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1475,6 +1475,10 @@ export class KallichoreSession implements JupyterLanguageRuntimeSession {
}
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

/**
* Performs a restart of the kernel. Kallichore handles the mechanics of
* stopping the process and starting a new one; we just need to listen for
Expand Down
4 changes: 4 additions & 0 deletions extensions/positron-zed/src/positronZedLanguageRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1192,6 +1192,10 @@ export class PositronZedRuntimeSession implements positron.LanguageRuntimeSessio
}
}

getDynState(): Thenable<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

/**
* Restarts the runtime.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ class TestLanguageRuntimeSession implements positron.LanguageRuntimeSession {
return this._runtimeInfo;
}

getDynState(): Promise<positron.LanguageRuntimeDynState> {
return Promise.resolve(this.dynState);
}

generateMessageId(): string {
return `msg-${TestLanguageRuntimeSession.messageId++}`;
}
Expand Down
106 changes: 64 additions & 42 deletions src/positron-dts/positron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1036,12 +1036,11 @@ declare module 'positron' {
}

/**
* LanguageRuntimeSession is an interface implemented by extensions that provide a
* set of common tools for interacting with a language runtime, such as code
* execution, LSP implementation, and plotting.
* Basic metadata about an active language runtime session, including
* immutable metadata about the session itself and metadata about the
* runtime with which it is associated.
*/
export interface LanguageRuntimeSession extends vscode.Disposable {

export interface ActiveRuntimeSessionMetadata {
/** An object supplying immutable metadata about this specific session */
readonly metadata: RuntimeSessionMetadata;

Expand All @@ -1050,12 +1049,61 @@ declare module 'positron' {
* session is associated.
*/
readonly runtimeMetadata: LanguageRuntimeMetadata;
}

/** Information about the runtime that is only available after starting. */
readonly runtimeInfo: LanguageRuntimeInfo | undefined;
/**
* Base interface for a language runtime session.
*
* This is the version of a language runtime session that is returned by
* Positron's API methods; it provides basic access to the session for use
* by extensions other that the one that created the session.
*/
export interface BaseLanguageRuntimeSession extends ActiveRuntimeSessionMetadata {

/** The state of the runtime that changes during a user session */
dynState: LanguageRuntimeDynState;
getDynState(): Thenable<LanguageRuntimeDynState>;

/**
* Calls a method in the runtime and returns the result.
*
* Throws a RuntimeMethodError if the method call fails.
*
* @param method The name of the method to call
* @param args Arguments to pass to the method
*/
callMethod?(method: string, ...args: any[]): Thenable<any>;

/**
* Execute code in the runtime
*
* @param code The code to execute
* @param id The ID of the code
* @param mode The code execution mode
* @param errorBehavior The code execution error behavior
* Note: The errorBehavior parameter is currently ignored by kernels
*/
execute(code: string,
id: string,
mode: RuntimeCodeExecutionMode,
errorBehavior: RuntimeErrorBehavior): void;

/**
* Shut down the runtime; returns a Thenable that resolves when the
* runtime shutdown sequence has been successfully started (not
* necessarily when it has completed).
*/
shutdown(exitReason: RuntimeExitReason): Thenable<void>;
}

/**
* LanguageRuntimeSession is the full interface implemented by extensions
* that provide a set of common tools for interacting with a language
* runtime, such as code execution, LSP implementation, and plotting.
*/
export interface LanguageRuntimeSession extends BaseLanguageRuntimeSession, vscode.Disposable {

/** Information about the runtime that is only available after starting. */
readonly runtimeInfo: LanguageRuntimeInfo | undefined;

/** An object that emits language runtime events */
onDidReceiveRuntimeMessage: vscode.Event<LanguageRuntimeMessage>;
Expand All @@ -1081,30 +1129,6 @@ declare module 'positron' {
*/
debug(request: DebugProtocolRequest): Thenable<DebugProtocolResponse>;

/**
* Execute code in the runtime
*
* @param code The code to execute
* @param id The ID of the code
* @param mode The code execution mode
* @param errorBehavior The code execution error behavior
* Note: The errorBehavior parameter is currently ignored by kernels
*/
execute(code: string,
id: string,
mode: RuntimeCodeExecutionMode,
errorBehavior: RuntimeErrorBehavior): void;

/**
* Calls a method in the runtime and returns the result.
*
* Throws a RuntimeMethodError if the method call fails.
*
* @param method The name of the method to call
* @param args Arguments to pass to the method
*/
callMethod?(method: string, ...args: any[]): Thenable<any>;

/** Test a code fragment for completeness */
isCodeFragmentComplete(code: string): Thenable<RuntimeCodeFragmentStatus>;

Expand Down Expand Up @@ -1173,13 +1197,6 @@ declare module 'positron' {
*/
restart(workingDirectory?: string): Thenable<void>;

/**
* Shut down the runtime; returns a Thenable that resolves when the
* runtime shutdown sequence has been successfully started (not
* necessarily when it has completed).
*/
shutdown(exitReason: RuntimeExitReason): Thenable<void>;

/**
* Forcibly quits the runtime; returns a Thenable that resolves when the
* runtime has been terminated. This may be called by Positron if the
Expand Down Expand Up @@ -1812,19 +1829,24 @@ declare module 'positron' {
/**
* List all active sessions.
*/
export function getActiveSessions(): Thenable<LanguageRuntimeSession[]>;
export function getActiveSessions(): Thenable<BaseLanguageRuntimeSession[]>;

/**
* Get a specific session by its ID.
*/
export function getSession(sessionId: string): Thenable<BaseLanguageRuntimeSession | undefined>;

/**
* Get the active foreground session, if any.
*/
export function getForegroundSession(): Thenable<LanguageRuntimeSession | undefined>;
export function getForegroundSession(): Thenable<BaseLanguageRuntimeSession | undefined>;

/**
* Get the session corresponding to a notebook, if any.
*
* @param notebookUri The URI of the notebook.
*/
export function getNotebookSession(notebookUri: vscode.Uri): Thenable<LanguageRuntimeSession | undefined>;
export function getNotebookSession(notebookUri: vscode.Uri): Thenable<BaseLanguageRuntimeSession | undefined>;

/**
* Select and start a runtime previously registered with Positron. Any
Expand Down
Loading
Loading