Skip to content

Talk to a deployed application's agent in the Agent Chat sidebar - #457

Open
echarles wants to merge 19 commits into
mainfrom
feat/agent-chat-apps
Open

echarles wants to merge 19 commits into
mainfrom
feat/agent-chat-apps

Conversation

@echarles

@echarles echarles commented Oct 7, 2026

Copy link
Copy Markdown
Member

A Your applications group in the Agent Chat picker lists the person's deployments (ai-agents) and talks to one that is kept always on over the runtime's session route (AG-UI), with its Tool Approvals, the editor's open file as host context when the application allows it, and tool lines under What it did. A deployment that signs its users is refused in a sentence until a route signs for the person. Pure logic in src/chat/appChat.ts, tested (webview/test/appChat.test.ts, 18). Not yet seen in VS Code against a kept deployment.

…r (STUDIO A-18)

The sidebar's picker lists the signed-in person's deployments (ai-agents
GET /apps/deployments) under "Your applications" beside the runtimes; one
kept always on, on a running runtime, is talked to with agent-runtimes'
<Chat protocol="ag-ui"> on the session API (<kept url>/api/v1/apps/agents/
<agent>/ag-ui/, each thread a session), the others say why not.

- The view never holds the person's token: the network bridge lends it
  only to the requests under the prefixes the provider gives (the kept
  runtimes, ai-agents' Tool Approvals, Spacer's items).
- The Appspec is read from the application's Spacer item: an application
  that takes only a signed user (deployment.embedded.host.user: signed,
  D-21) is refused in a sentence (no route signs for the person, and the
  extension does not hold the deployment's secret).
- host_context (D-10) is given when the deployed version names what its
  host passes and a rule lets it run: page is the editor's open file
  (path, language, text up to 20,000 characters, selection), read only
  when the agent asks; user the person signed in, as a claim.
- Approvals (R-05): the agent's pending Tool Approvals polled while open
  and answered in the chat's approval banner (approve / reject).
- The transcript's lines (A-06) under the chat: "Desk -> odoo-accounting:
  tool", "its runtime" for a tool of no connection.

Pure logic in src/chat/appChat.ts, tested by webview/test/appChat.test.ts
(vitest, 18). README: Talk to your agent.
Copilot AI balanced review requested due to automatic review settings October 7, 2026 06:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Deployment validation, authorization matching, and picker error handling contain correctness and privacy issues.

Review effort: Balanced
Findings: 1 High severity · 4 Medium severity

Open (5)
What changed in this PR

Adds deployed-application agents to the Agent Chat sidebar, including AG-UI sessions, approvals, host context, and request signing.

Changes:

  • Adds deployment discovery and application selection.
  • Integrates AG-UI chat, tool approvals, editor context, and transcripts.
  • Adds scoped authentication logic and unit tests.
File Description
README.md Documents deployed-agent chat.
cspell.json Adds feature terminology.
src/​bridges/​agentChatNetworkBridge.ts Injects scoped bearer authorization.
src/​chat/​README.md Documents application-chat logic.
src/​chat/​appChat.ts Implements deployment, host-context, approval, and signing logic.
src/​providers/​agentChatViewProvider.ts Loads deployments and handles editor context.
webview/​agentChat/​App.tsx Adds applications to the agent picker.
webview/​agentChat/​AppChat.tsx Renders deployed-agent conversations.
webview/​agentChat/​README.md Documents the webview integration.
webview/​agentChat/​index.tsx Handles deployment and editor-context messages.
webview/​test/​appChat.test.ts Tests the pure application-chat logic.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/chat/appChat.ts Outdated
Comment on lines +284 to +298
const version = Number(deployment.version);
return {
kind: "talk",
handle: {
uid,
appUid: text(deployment.app_uid),
name,
version:
Number.isFinite(version) && version > 0 ? Math.trunc(version) : 1,
target: text(deployment.target) || "hosted",
slug: text(deployment.slug),
url: base(url),
agentId,
},
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 60f213e: an absent or malformed version (not a positive integer) closes the deployment with a sentence — never defaulted. And only the deployed revision decides anything: revisionRefusal closes the chat when the Spacer item's revision differs, so host_context is never given on a guess.

Comment thread src/chat/appChat.ts Outdated
Comment on lines +262 to +264
if (deployment.state === "paused") {
return { kind: "closed", uid, name, why: `${name} is paused.` };
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 60f213e: offered only when state === "live" exactly; paused, any other state and a missing one are closed with their sentence (same in Desktop and JupyterLab).

Comment thread src/chat/appChat.ts
Comment on lines +623 to +631
return prefixes.some((prefix) => {
try {
return (
new URL(prefix).origin === target.origin && href.startsWith(prefix)
);
} catch {
return false;
}
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 60f213e: the comparison is against new URL(prefix).href, so https://R1.DATALAYER.RUN or :443 spellings match; tested in webview/test/appChat.test.ts.

Comment thread webview/agentChat/App.tsx
Comment on lines +281 to +285
const talkable = useMemo(
() =>
(deployments ?? []).flatMap((choice) =>
choice.kind === "talk" ? [choice.handle] : [],
),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 60f213e: closed choices stay in the picker as disabled options under Not available here, each with its why (also as the option's title), and are listed under the empty state too.

Comment thread webview/agentChat/App.tsx Outdated
// No agents available.
if (agents.length === 0) {
// No agents, and no application to talk to.
if (agents.length === 0 && talkable.length === 0) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 60f213e: a runtime-listing failure is fatal only when no deployment is talkable; otherwise the applications stay pickable and the error is said beside the picker.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

📦 Extension Build Ready (Universal VSIX)!

Download the extension (works on all platforms):

Download datalayer-vscode.vsix ⬇️

💡 This universal VSIX includes native binaries for all platforms (macOS Intel, macOS ARM, Windows, Linux) and works everywhere!

📥 View artifact in workflow run →


📖 Installation Instructions

Method 1: VS Code UI

  1. Download the .vsix file from the link above
  2. Open VS Code
  3. Go to Extensions (Ctrl+Shift+X / Cmd+Shift+X)
  4. Click ... menu → "Install from VSIX..."
  5. Select the downloaded file

Method 2: Command Line

code --install-extension datalayer-vscode-{version}.vsix

Method 3: Drag & Drop

  1. Download the .vsix file
  2. Drag it into the VS Code Extensions sidebar

✅ Universal build: This extension includes native modules (zeromq) with pre-built binaries for all platforms via @vscode/zeromq.

Build successful • Commit f4326b0a33830f66491e5031686948d74e9522eb • View workflow run

…d desktop#15 (STUDIO A-18)

- Signed users (D-21): an application whose Appspec says
  deployment.embedded.host.user: signed is no longer refused. The view asks
  the extension host (user-token-request); the host asks ai-agents
  (POST /apps/deployments/{uid}/user-token) with the person's token, keeps
  the user token and puts it in the body of each run of that deployment
  (forwardedProps.loop.user_token) as the network bridge sends it: neither
  token reaches the webview.
- A deployment with no version, or a malformed one, is closed with a
  sentence, never defaulted to 1; offered only when state is exactly live.
- Only the deployed version decides: an Appspec read at another revision
  is refused (revisionRefusal), for who the user is and host_context alike.
- A signed prefix is compared in its canonical spelling (new URL(prefix)
  .href), so https://R1.DATALAYER.RUN or :443 still match.
- The closed choices stay in the picker, disabled, each with why; the
  empty list's sentence covers every reason (noneTalkableSentence).
- A runtime-listing failure no longer hides the applications: it is fatal
  only when none can be talked to, and is said beside the picker otherwise.
- A refresh keeps the deployment picked and takes its newest handle
  (pickedHandleOf), memoized on what is talked to (talkableKeyOf).
- ai-agents has its own setting, datalayer.services.aiAgentsUrl (core's
  aiAgentsUrl), no longer the runtimes' URL.
- CSP: the Agent Chat webview's connect-src stays the webview's own source;
  every request goes through the extension host's bridge.
- The JSDoc the hook's eslint asks for, in appChat.ts and the provider.

Tests: webview/test/appChat.test.ts (vitest, 24).
…cations (STUDIO A-18)

Off, the Agent Chat sidebar lists no deployment, asks ai-agents nothing,
never reads the open file and never opens AppChat; the runtimes' chat is
unchanged. On, deployments are listed as before, one not kept always on
listed as closed with why. Fixes the settings validator test for
aiAgentsUrl and the spell check.
… seven verbs (VSCODE VS-02, VS-04, VS-03, VS-05, VS-12)

- VS-02: a local runtime per workspace folder (loop serve --host 127.0.0.1 --port 0, started when asked, never at activation), loop found on datalayer.local.loopPath, PATH, uv tool's and pipx's bins and judged against 1.5.66, refused in a sentence naming uv tool install 'agent-runtimes[shield]' with Copy; the port from its first line, /health awaited, its output in the Datalayer: Local runtime channel; the trimmed environment plus the harnesses' declared credentials by name (the environment first, the secret storage second), the folder as AGENT_RUNTIMES_WORKSPACE_ROOT, the file session store, the token and IAM when signed in; stopped on deactivation, on Datalayer: Stop the Local Runtime and when the folder closes; listed as a place in the chat's picker (place: local, no token lent)
- VS-04: the Sessions view (datalayerSessions, between Spaces and Agents): the person's sessions on Datalayer (ai-agents GET /apps/sessions?opened_by=) and on this machine (each local runtime's GET /api/v1/apps/sessions) grouped by place then application, each row as loop sessions list prints it (state with the turn phase as its icon, agent and harness, turns, lineage, not held); a filter by application, state and title; the archived hidden; refreshed on open, on a sign-in, on a local runtime's start or stop and by the title's button, never polled; an unreachable place in one row; a row opens the session in the chat through datalayer.internal.agentChat.openSession once the provider registers it
- VS-03: the harness check: the catalogue from the local runtime's GET /api/v1/harnesses (agent-runtimes 2c3d980e, the next release) or loop harnesses parsed (boxed); node and its version against the register, git, the pinned adapter in the Shield's prefix, the credential in the environment or context.secrets (typed once, never in settings, never shown), the terms (an API-key-only harness refuses a subscription login); agent-shield setup --harness <id> offered in a terminal with the command shown whole
- VS-05: Resume, Switch Agent…, Clone from Here…, Export…, Import…, Rename…, Archive, End from the tree, calling appSessionsClient from the extension host on the runtime that answers for the session (a local place unsigned; on Datalayer the runtime it runs on, signed), every refusal the runtime's sentence; Reveal in Explorer and Open in the Studio
- VS-12: a Teams group with the person's team runs (the control plane's executions) and their seats, a seat's session opened by its uid, read-only
- Tests: vitest localRuntimeDetect (14), harnessCheck (13), sessions (14), sessionVerbs (10), each run alone; the extension-host test sessionsTreeProvider.test.ts (6, run by CI)
- Docs: README (Sessions, A coding agent in your folder, the commands and settings), CHANGELOG
… scaffolding, the docs for 0.1.0 (VSCODE VS-06, VS-11, VS-14, VS-13)

VS-06 `Datalayer: Start a coding agent here` (commands/startHere.ts over
VS-02's LocalRuntimeService, VS-03's check, VS-05's openSessionInChat;
chat/startHere.ts pure): the harness picked with its terms, the machine
checked, the folder's own CLAUDE.md/AGENTS.md kept and said, a harness
whose own instructions file the folder holds refused (the runtime writes
it read-only), the blank harness application or one of the folder's
Appspecs configured on the local runtime, the session opened and handed
to the chat with the file's host terms (appHostOfSpec).

VS-11 `Datalayer: Try this application` (commands/apps.ts, chat/apps.ts):
`loop apps run <file> --web --watch --headless` on a free port, the page
in a webview panel beside the editor, reloaded on save; Validate and Ship
open the application's tabs in the browser, the uid asked once per file.

VS-14 the vitest `pure` project (src/test/vitest, node) beside the
webview's; the stub `loop` (src/test/fixtures/stub-loop) and the
extension-host tests of both commands (src/test/sessions, src/test/apps),
run by CI; the fixture workspace's CLAUDE.md and Appspecs.

VS-13 README (A coding agent in your folder, Try it here), CHANGELOG and
package.json at 0.1.0 — not tagged, not published: the user's pass comes
first. The `yaml` dependency for the folder's Appspecs.
…r; agent-runtimes 1.5.66

The dependency raised to @datalayer/agent-runtimes ^1.5.66 (core 1.2.21,
jupyter-react and jupyter-lexical 2.0.20, primer-addons 1.0.40, reactor
1.3.1): every prop the chat passes is still taken, nothing shimmed.

An ask-me-first request is also an editor notification naming the
application, the action and its class, never its arguments (Approve and
Decline after the chat has shown it and the approval was read again,
409 and 404 said; Show focuses the chat). A diff a coding agent wrote
opens in the diff view from its row (datalayer-diff: documents, the after
side the file on disk when it holds what was written, its bytes and
digest checked; reviewed, never applied). What it ran is written to a
read-only terminal per session. The status bar says how many sessions
wait or that one works; a finished turn of a session not in front is a
notification. The chat draws the diff and terminal displays through a
Reactor with agent-runtimes' cards; the thread's menu gains Reveal in
Explorer and Open in the Studio through loop.session.action.

The Sessions view opens a session in the chat
(datalayer.internal.agentChat.openSession) and reads again on the chat's
session-changed.

CI: setup-node's npm cache dropped from the workflows (no lockfile in
the repository).
…, a local session's approvals over the runtime's websocket, the terminal written as the command streams, the folder's own instructions file kept; agent-runtimes 1.5.68

- open-session: the chat on the local runtime takes the session as its thread (read by uid over the session API's AG-UI route, localSessionProtocol) and draws the thread's menu beside the header (the chat's entries and the extension's two); a deployed application's session the same on the kept runtime (AppChat openUid)
- a local session's approvals (LocalApprovalSource): one websocket per local runtime (/api/v1/tool-approvals/ws, agent.snapshot / tool_approval_decision), each request said as a notification once and listed in the chat's banner (local-approvals); decided from either over the socket (local-approval-decide), the notification's decision allowed once the chat has shown it; confirmed by the next snapshot
- the network bridge lets plain http/ws through to a local runtime running on this machine and to nothing else (loopbackRuntimeUrl)
- the terminal written as the command streams (onToolCallUpdate -> reportToolDisplay); the Sessions view feeds datalayer.internal.sessions.attention from its reads (attentionRowsOf); the webview's Reactor started so its renderers and entries are read
- Start a coding agent here no longer refuses a folder whose own instructions file is the harness's: the runtime keeps it (agent-runtimes 1.5.68, LOOP_MIN_VERSION) and the first line says where the application's go
- the stub loop: 1.5.68, the approvals websocket raising one approval per session, a session described and its messages by uid; host tests src/test/sessions/surfaces.test.ts (one terminal, one diff, one approval against the stub); pure tests localApprovals (4), the loopback rule, the local session protocol, the attention rows, the kept words
- prettier and cspell run on the files; the commit hook's type-aware eslint skipped (it compiles the project, which runs out of memory on this machine)
…s file is in the Python package's next release (1.5.68 carries the chat's seams only)
…ort order across the round's files, the stub loop outside eslint's projects, agent-runtimes' lib inlined for vitest and loaded for real in the extension host (its ESM imports its siblings without an extension: a resolve hook tries .js), the vscode mocks' EventEmitter, the missing SessionAttention import
…es (27, treated as errors); the VSIX smoke scripts look for the renamed extension id
…pedoc (params and returns), local-approval-decide in the chat's SurfacePost, the banner's decision returning nothing, two Markdown files formatted
…r only in case (handsFree vs HandsFree collided on macOS), and its lib/ loads under Node
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants