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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/cli/ai/runtimes/pi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ async function getOrCreateAgent(
: { previewSteering: isForkedByDesktop, remoteSession }
);

const tools = buildAgentTools( config, isForkedByDesktop );
const tools = buildAgentTools( config, isForkedByDesktop, remoteSession );

// On a `/model` swap mid-session, reuse the prior transcript so the
// conversation continues; on a cold fork hydrate from the sidecar.
Expand Down Expand Up @@ -348,7 +348,8 @@ function buildAnthropicBearerStreamFn( creds: ResolvedCredentials ): StreamFn {

function buildAgentTools(
config: AgentRuntimeConfig,
enablePreviewSteering: boolean
enablePreviewSteering: boolean,
remoteSession: boolean
): AgentToolAny[] {
const isRemoteSite = Boolean(
config.activeSite?.remote && config.activeSite?.wpcomSiteId && config.wpcomAccessToken
Expand Down Expand Up @@ -390,7 +391,7 @@ function buildAgentTools(
renameTool( createLsTool( STUDIO_SITES_ROOT ), 'Ls' ),
];
return [
...resolveStudioToolDefinitions( { enablePreviewSteering } ),
...resolveStudioToolDefinitions( { enablePreviewSteering, remoteSession } ),
...askUserTool,
...skillTool,
...piTools,
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/ai/system-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ const REMOTE_SESSION_GUIDANCE = `## Telegram remote session

You are running over Telegram. The user iterates turn-by-turn; keep replies short and image-driven.

After ANY visible change to a site, call \`share_screenshot\` before ending the turn — no preamble, no permission-asking. It is fire-and-forget: the image goes to the user but is NOT returned to you. Do not analyze or describe what you sent. Follow up with at most one short sentence (e.g. "Heading is now red." or "Want me to publish this as a preview?").
When the user explicitly asks to see the site, or when you finish a logical milestone with a clear visible result, call \`share_screenshot\` before ending the turn — no preamble, no permission-asking. One screenshot per milestone, not per edit: don't pepper the user with intermediate snapshots while you iterate. It is fire-and-forget: the image goes to the user but is NOT returned to you. Do not analyze or describe what you sent. Follow up with at most one short sentence (e.g. "Heading is now red." or "Want me to publish this as a preview?").

Defaults to a 16:9 above-the-fold view. Pass \`fullPage: true\` only when the user explicitly asks for the whole page. Captions describe what the user is looking at; never mention "full page", "viewport", or other capture-mode wording.

Expand Down
34 changes: 14 additions & 20 deletions apps/cli/ai/tests/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,33 +191,27 @@ describe( 'Studio AI MCP tools', () => {
} );

describe( 'share_screenshot gating', () => {
const originalValue = process.env.STUDIO_ENABLE_REMOTE_SESSION;

beforeEach( () => {
delete process.env.STUDIO_ENABLE_REMOTE_SESSION;
} );

afterEach( () => {
if ( originalValue === undefined ) {
delete process.env.STUDIO_ENABLE_REMOTE_SESSION;
} else {
process.env.STUDIO_ENABLE_REMOTE_SESSION = originalValue;
}
} );

it( 'omits share_screenshot when STUDIO_ENABLE_REMOTE_SESSION is unset', () => {
it( 'omits share_screenshot when remoteSession is not set', () => {
const names = resolveStudioToolDefinitions( { enablePreviewSteering: true } ).map(
( tool ) => tool.name
);
expect( names ).not.toContain( 'share_screenshot' );
expect( names ).toContain( 'take_screenshot' );
} );

it( 'includes share_screenshot when STUDIO_ENABLE_REMOTE_SESSION=true', () => {
process.env.STUDIO_ENABLE_REMOTE_SESSION = 'true';
const names = resolveStudioToolDefinitions( { enablePreviewSteering: true } ).map(
( tool ) => tool.name
);
it( 'omits share_screenshot when remoteSession is false', () => {
const names = resolveStudioToolDefinitions( {
enablePreviewSteering: true,
remoteSession: false,
} ).map( ( tool ) => tool.name );
expect( names ).not.toContain( 'share_screenshot' );
} );

it( 'includes share_screenshot when remoteSession is true', () => {
const names = resolveStudioToolDefinitions( {
enablePreviewSteering: true,
remoteSession: true,
} ).map( ( tool ) => tool.name );
expect( names ).toContain( 'share_screenshot' );
} );
} );
Expand Down
10 changes: 8 additions & 2 deletions apps/cli/ai/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isRemoteSessionEnabled } from 'cli/lib/feature-flags';
import { createPreviewTool } from './create-preview';
import { createSiteTool } from './create-site';
import { deletePreviewTool } from './delete-preview';
Expand Down Expand Up @@ -70,6 +69,13 @@ export interface CreateStudioToolsOptions {
// available). Defaults to false so standalone CLI runs don't advertise
// tools whose side effects would vanish into the void.
enablePreviewSteering?: boolean;
// Enable share_screenshot. Only meaningful when the agent is actually
// being driven by the remote-session daemon (Telegram bridge), signaled
// by `STUDIO_REMOTE_SESSION=1`. The `STUDIO_ENABLE_REMOTE_SESSION`
// feature flag only opts users into the `remote-session` command — it
// must NOT also expose share_screenshot to direct `studio code`
// invocations, where the image has nowhere to go.
remoteSession?: boolean;
}

export function resolveStudioToolDefinitions( options: CreateStudioToolsOptions = {} ) {
Expand All @@ -79,7 +85,7 @@ export function resolveStudioToolDefinitions( options: CreateStudioToolsOptions
excludedNames.add( t.name );
}
}
if ( ! isRemoteSessionEnabled() ) {
if ( ! options.remoteSession ) {
excludedNames.add( shareScreenshotTool.name );
}
if ( excludedNames.size === 0 ) {
Expand Down
9 changes: 6 additions & 3 deletions apps/cli/ai/tools/share-screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import { textResult } from './utils';
* agent never sees the image — the user does, and the agent gets only a
* confirmation string back.
*
* Only registered when the remote-session feature flag is on; the regular
* desktop / interactive CLI flows don't expose this tool.
* Only registered when the agent is actually being driven by the
* remote-session daemon (`STUDIO_REMOTE_SESSION=1`); the regular desktop /
* interactive CLI flows don't expose this tool, even when the
* `STUDIO_ENABLE_REMOTE_SESSION` feature flag is on.
*/
export const shareScreenshotTool = defineTool(
'share_screenshot',
'Fire-and-forget primitive that captures a URL and delivers the image to the user. ' +
'Call after ANY visible change to a site so the user sees the new state. ' +
'Use this when the user explicitly asks to see the site, or when you finish a logical milestone with a clear visible result worth looking at — not after every intermediate edit. ' +
'One screenshot per milestone, not per tool call. If the change is non-visual (data, listings, logs), skip the screenshot and reply in text. ' +
Comment on lines +26 to +27

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for improving the prompt and the description, it makes sense to only take a screenshot witht these conditions. 👍

'Returns a confirmation string only — the image is NOT returned to you. ' +
'The user already has the picture; do not analyze or describe what was sent in your reply. ' +
'After calling this, write at most one short follow-up sentence and end the turn. ' +
Expand Down
Loading