Repository navigation
feat: enable sharing of chat sessions - #1935
Conversation
Closes kagent-dev#1933 Signed-off-by: Brian Fox <[email protected]>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds end-to-end “shared session” support via share tokens, including UI affordances to create/manage share links, frontend propagation of X-Share-Token, and backend persistence + enforcement (read-only vs read-write) for shared access.
Changes:
- UI: create/manage share links, show shared-session indicators, and propagate share tokens through chat/task/A2A calls.
- Backend (Go): add session share tables, middleware validation/enforcement, share CRUD endpoints, and session listing enhancements with share metadata.
- Agent runtimes (Python/Go ADK) + Helm/CRDs: optional “share tools” injection and
KAGENT_UI_URLplumbing for generating clickable share URLs.
Reviewed changes
Copilot reviewed 54 out of 59 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/types/index.ts | Extends Session type with optional share_token and share_read_only. |
| ui/src/lib/auth.ts | Forwards x-share-token header by default. |
| ui/src/lib/a2aClient.ts | Allows attaching X-Share-Token to streaming A2A requests; tightens types. |
| ui/src/lib/tests/auth.test.ts | Updates default-forward-header expectations for share token forwarding. |
| ui/src/components/sidebars/SessionGroup.tsx | Passes share metadata down to chat items. |
| ui/src/components/sidebars/ChatItem.tsx | Appends ?share=... to shared-session links and shows shared icon. |
| ui/src/components/chat/ShareButton.tsx | New client component to create/copy/revoke share links. |
| ui/src/components/chat/ChatMessage.tsx | Simplifies ask-user submit callback wiring. |
| ui/src/components/chat/ChatInterface.tsx | Propagates share token through reads/A2A and enforces read-only UI mode; adds ShareButton. |
| ui/src/components/chat/AskUserDisplay.tsx | Disables ask-user interactions when handlers are absent (read-only mode). |
| ui/src/components/ToolDisplay.tsx | Disables approve/reject buttons when no handlers (read-only mode). |
| ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx | Reads share query param and passes it to ChatInterface (with Suspense). |
| ui/src/app/actions/sessions.ts | Adds optional shareToken header support for session/task fetches + getSessionWithEvents. |
| ui/src/app/actions/sessionShares.ts | New server actions for session share CRUD. |
| python/packages/kagent-adk/tests/unittests/test_share_tools.py | Adds unit tests for Python share tools. |
| python/packages/kagent-adk/src/kagent/adk/types.py | Adds share_tools flag to AgentConfig and injects share tools when enabled. |
| python/packages/kagent-adk/src/kagent/adk/tools/share_tools.py | Implements Python ADK share tools and URL generation. |
| python/packages/kagent-adk/src/kagent/adk/tools/init.py | Exports the new Python share tools. |
| helm/kagent/values.yaml | Adds controller.externalUrl for setting KAGENT_UI_URL. |
| helm/kagent/templates/controller-deployment.yaml | Injects KAGENT_UI_URL into controller when configured. |
| helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml | Adds shareTools field to rendered CRD schema. |
| helm/kagent-crds/templates/kagent.dev_agents.yaml | Adds shareTools field to rendered CRD schema. |
| go/core/pkg/migrations/core/000005_session_shares.up.sql | Adds session_share and session_share_access tables. |
| go/core/pkg/migrations/core/000005_session_shares.down.sql | Adds down migration (currently only drops one table). |
| go/core/pkg/env/kagent.go | Registers KAGENT_UI_URL env var. |
| go/core/pkg/auth/share_test.go | Unit tests for ShareContext helpers. |
| go/core/pkg/auth/share.go | Adds ShareContext storage/retrieval in request context. |
| go/core/internal/httpserver/server_share_middleware_test.go | Adds tests for share-token middleware behavior. |
| go/core/internal/httpserver/server.go | Registers share routes and share-token middleware. |
| go/core/internal/httpserver/middleware.go | Implements share-token validation, read-only enforcement, and access recording. |
| go/core/internal/httpserver/handlers/sessions_test.go | Extends session handler tests for read_only field + updated list type. |
| go/core/internal/httpserver/handlers/sessions_share_context_test.go | Adds tests for effective user ID resolution under share context. |
| go/core/internal/httpserver/handlers/sessions.go | Uses ShareContext for DB lookups + returns read_only in responses. |
| go/core/internal/httpserver/handlers/session_shares_test.go | Adds tests for session share CRUD handlers. |
| go/core/internal/httpserver/handlers/session_shares.go | Implements session share CRUD endpoints. |
| go/core/internal/httpserver/handlers/handlers.go | Wires SessionSharesHandler into handler set. |
| go/core/internal/httpserver/auth/authn.go | Forces agent upstream calls to use share owner’s X-User-Id when share context present. |
| go/core/internal/database/queries/sessions.sql | Enhances session listing to include shared sessions and share metadata. |
| go/core/internal/database/queries/session_shares.sql | Adds SQL queries for share CRUD and access upsert. |
| go/core/internal/database/client_postgres.go | Implements DB methods for shares + new session list return type. |
| go/core/internal/controller/translator/agent/manifest_builder.go | Propagates KAGENT_UI_URL into agent pods when set. |
| go/core/internal/controller/translator/agent/compiler.go | Passes shareTools flag into ADK AgentConfig. |
| go/core/internal/a2a/manager_test.go | Tests initiated_by injection when share context is present. |
| go/core/internal/a2a/manager.go | Injects initiated_by metadata for shared-session A2A messages. |
| go/api/v1alpha2/agent_types.go | Adds shareTools field to Agent CRD Go types. |
| go/api/database/models.go | Adds SessionWithShareToken and SessionShare models. |
| go/api/database/client.go | Extends DB client interface with share methods and updated session list signature. |
| go/api/config/crd/bases/kagent.dev_sandboxagents.yaml | Adds shareTools to generated CRD base. |
| go/api/config/crd/bases/kagent.dev_agents.yaml | Adds shareTools to generated CRD base. |
| go/api/adk/types.go | Adds share_tools to ADK AgentConfig JSON handling. |
| go/adk/pkg/tools/share_tools_test.go | Adds tests for Go ADK share tools. |
| go/adk/pkg/tools/share_tools.go | Implements Go ADK share tools and URL generation. |
| go/adk/pkg/runner/adapter.go | Conditionally injects share tools into Go ADK runner. |
| go/adk/cmd/main.go | Passes HTTP client + kagent URL into runner config for share tools. |
Files not reviewed (5)
- go/api/v1alpha2/zz_generated.deepcopy.go: Language not supported
- go/core/internal/database/gen/models.go: Language not supported
- go/core/internal/database/gen/querier.go: Language not supported
- go/core/internal/database/gen/session_shares.sql.go: Language not supported
- go/core/internal/database/gen/sessions.sql.go: Language not supported
Comments suppressed due to low confidence (8)
go/core/pkg/migrations/core/000005_session_shares.down.sql:1
- The down migration drops
session_sharebut notsession_share_access. Sincesession_share_accesshas a foreign key referencingsession_share, droppingsession_sharefirst will fail in Postgres (or leave orphaned objects depending on settings). Dropsession_share_accessfirst, thensession_share(and consider dropping the index too for completeness).
ui/src/components/chat/ShareButton.tsx:1 - This references
windowduring render. In Next.js App Router, client components can still be pre-rendered on the server, wherewindowis undefined, causing a runtime crash. Computeorigininside an effect (store in state), or guard withtypeof window !== \"undefined\"before readingwindow.location.origin.
ui/src/components/chat/ChatInterface.tsx:1 shareReadOnlyis only set inside theshareTokenbranch. If the user navigates from a shared read-only session to a non-shared session without a full remount,shareReadOnlycan remaintrueand incorrectly disable interactions. ResetshareReadOnlytofalsewhenshareTokenis absent (e.g., at the start of initialization or in theelsebranch).
ui/src/components/chat/ChatInterface.tsx:1shareReadOnlyis only set inside theshareTokenbranch. If the user navigates from a shared read-only session to a non-shared session without a full remount,shareReadOnlycan remaintrueand incorrectly disable interactions. ResetshareReadOnlytofalsewhenshareTokenis absent (e.g., at the start of initialization or in theelsebranch).
ui/src/components/chat/ChatInterface.tsx:1shareReadOnlyis only set inside theshareTokenbranch. If the user navigates from a shared read-only session to a non-shared session without a full remount,shareReadOnlycan remaintrueand incorrectly disable interactions. ResetshareReadOnlytofalsewhenshareTokenis absent (e.g., at the start of initialization or in theelsebranch).
ui/src/components/sidebars/ChatItem.tsx:1- The share token is inserted into the URL query string without encoding and will be stored in browser history and may leak via referrer headers/logs. At minimum, wrap the token with
encodeURIComponent(or build viaURLSearchParams) to prevent malformed URLs; consider using a URL fragment (e.g.#share=...) if feasible so the token is not emitted as an HTTP referrer, while still being readable by the client.
python/packages/kagent-adk/src/kagent/adk/tools/share_tools.py:1 - Docstring says that without
KAGENT_UI_URLthe function returns only the raw token, but the implementation returns a relative path containing?share=<token>. Update the docstring to match actual behavior (relative path vs raw token).
ui/src/components/chat/ShareButton.tsx:1 - This input has no accessible name (no associated
<Label>and noaria-label), which makes the share URL harder to discover for screen reader users. Add anaria-label(e.g. "Share link") or associate it with a visible label.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…tion session_share_access has a FK referencing session_share, so dropping session_share first fails in Postgres. Reverse the order. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
…TERAL ListSessionsForAgent was running two separate correlated subqueries per session row to fetch share_token and share_read_only, doubling the work for the planner. A single LATERAL join fetches both fields in one pass and also eliminates the redundant EXISTS check in the WHERE clause. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
The delete handler was calling DeleteSessionShare without first checking that the session belongs to the caller. A non-owner would receive 200 OK even though nothing was deleted. Added the same GetSession ownership check that create and list already perform, returning 404 on mismatch. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
shareReadOnly was only set inside the shareToken branch. Navigating from a read-only shared session to an owned session without a full remount left it stuck at true, incorrectly disabling interactions. Reset it to false at the start of initializeChat so the non-shared path is always clean. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
…input window.location.origin was read at render time, which crashes during Next.js server-side pre-rendering where window is undefined. Moved to a useEffect so origin is only read client-side. Also added aria-label to the read-only share URL input so screen readers can identify it. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
The docstring claimed the function returns the raw token when KAGENT_UI_URL is unset. In reality it returns a relative path with the share query param. Updated the docstring to accurately describe both branches. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
|
Noticed that the build failed after latest merge; can't replicate the failure locally though so maybe a retry will sort it out. |
And set on controller ConfigMap rather than directly on the Deployment Signed-off-by: Brian Fox <[email protected]>
Exposes the DeclarativeAgentSpec.shareTools field in the UI so agents can be configured to gain the built-in share link tools without needing to edit the CRD directly. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
…hare tools Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
main added 000005_a2a_protocol_version; avoid duplicate migration number. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
- Add min-w-0 to URL span so truncate works inside flex row - Fall back to execCommand copy when Clipboard API unavailable (HTTP/non-secure contexts) Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
Thanks for the review. Think I have addressed all your comments. Also merged main which required a bit of reworking due to the a2a libray migration. |
The middleware now distinguishes pgx.ErrNoRows (403) from other DB errors (500). Update the invalid/revoked token test cases to return pgx.ErrNoRows so they expect 403 as intended. Co-Authored-By: Claude Sonnet 4.6 <[email protected]> Signed-off-by: Brian Fox <[email protected]>
supreme-gg-gg
left a comment
There was a problem hiding this comment.
Thanks for addressing the comments quickly! lgtm overall
Signed-off-by: Brian Fox <[email protected]>
# Conflicts: # go/core/internal/httpserver/handlers/handlers.go # ui/src/app/actions/sessions.ts # ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx # ui/src/app/agents/new/page.tsx # ui/src/components/sidebars/ChatItem.tsx # ui/src/components/sidebars/SessionGroup.tsx
Signed-off-by: Eitan Yarmush <[email protected]>
`SessionShares` was declared on the `Handlers` struct and routed in server.go, but never constructed in `NewHandlers`, leaving it nil and resulting in a nil pointer dereference when any caller tried to make use of the endpoint. Seems to have somehow got lost in the final commits on kagent-dev#1935. Signed-off-by: Brian Fox <[email protected]>
`SessionShares` was declared on the `Handlers` struct and routed in server.go, but never constructed in `NewHandlers`, leaving it nil and resulting in a nil pointer dereference when any caller tried to make use of the endpoint. Seems to have somehow got lost in the final commits on #1935. Signed-off-by: Brian Fox <[email protected]> Co-authored-by: Peter Jausovec <[email protected]>
…dev#2363) `SessionShares` was declared on the `Handlers` struct and routed in server.go, but never constructed in `NewHandlers`, leaving it nil and resulting in a nil pointer dereference when any caller tried to make use of the endpoint. Seems to have somehow got lost in the final commits on kagent-dev#1935. Signed-off-by: Brian Fox <[email protected]> Co-authored-by: Peter Jausovec <[email protected]>
kagent 0.10.0 Created-by: HarmonybrewBot Commit-by: HarmonybrewBot Merged-by: HarmonybrewBot Description: Created by `brew bump` --- Created with `brew bump-formula-pr`.<details> <summary>release notes</summary> <pre><!-- Release notes generated using configuration in .github/release.yml at v0.10.0 --> ## What's Changed ### Features * feat(streaming): configurable nginx, controller, and UI inactivity ti… by @yanivmn in kagent-dev/kagent#1973 * feat(helm): add controller service annotations by @anthonyhaussman in kagent-dev/kagent#2088 * feat: enable sharing of chat sessions by @onematchfox in kagent-dev/kagent#1935 * feat: substrate support for BYO and python runtimes for SandboxAgent CR by @jmhbh in kagent-dev/kagent#2043 * feat(substrate): bump agent substrate to 0.0.7 by @supreme-gg-gg in kagent-dev/kagent#2109 * feat(substrate): bump agent substrate to 0.0.8 by @jjamroga in kagent-dev/kagent#2140 * feat(helm/kagent): support deployment annotations by @anthonyhaussman in kagent-dev/kagent#2143 * feat: add nodeSelector support to agent helm charts by @the-mentor in kagent-dev/kagent#2145 * feat: expose icon, provider, documentation URL and version on agent A2A card by @QuentinBisson in kagent-dev/kagent#2188 * feat: add claude ACP sandbox target by @the-mentor in kagent-dev/kagent#2189 * feat(sandbox-agents): Store session state in `durableDir` volume for declarative sandbox agents by @jmhbh in kagent-dev/kagent#2171 * feat(helm): add UI HTTPRoute by @younsl in kagent-dev/kagent#2165 * feat(helm): bump bundled oauth2-proxy subchart to ~10.7.0 by @elihai-risk in kagent-dev/kagent#2254 * feat: default nodeSelector for agent deployments (--default-agent-node-selector) by @elihai-risk in kagent-dev/kagent#2259 * feat(helm): add podLabels for controller and UI pod templates by @elihai-risk in kagent-dev/kagent#2237 * feat(helm): add extraObjects for arbitrary manifests by @younsl in kagent-dev/kagent#2174 * feat(ui-test): Playwright ui tests foundation stage 0 by @cjlg-soloio in kagent-dev/kagent#2262 * feat: allow read-only shares to read A2A tasks by @QuentinBisson in kagent-dev/kagent#2191 * feat(controller): make Go (ADK) runtime agent image independently configurable by @QuentinBisson in kagent-dev/kagent#2021 * feat(helm): allow custom annotations on the default ModelConfig by @marians in kagent-dev/kagent#2201 * feat(ui-test): Playwright UI E2E suite — helpers + feature flows (stage 2) by @cjlg-soloio in kagent-dev/kagent#2264 * feat: serve A2A ListTasks from the task store by @QuentinBisson in kagent-dev/kagent#2187 * feat(models): add maxCompletionTokens for OpenAI by @anthonytwh in kagent-dev/kagent#2175 * feat(models): wire maxOutputTokens for native Gemini and Vertex by @anthonytwh in kagent-dev/kagent#2147 * feat: add envFrom support to agent deployment spec by @uncycler in kagent-dev/kagent#2321 * feat: add native AWS Bedrock Guardrails support to ModelConfig by @sanskar-singh-2403 in kagent-dev/kagent#2323 * feat(bedrock): configurable Bedrock read/connect timeout by @go-faustino in kagent-dev/kagent#2223 * feat: add deploymentAnnotations to agent deployment spec by @anthonyhaussman in kagent-dev/kagent#2359 * feat(helm/kagent): allow disabling default modelconfig by @anthonyhaussman in kagent-dev/kagent#2142 * feat(sts): send RFC 8707 resource and audience on token exchange by @QuentinBisson in kagent-dev/kagent#2106 * feat(sts): send RFC 8707 resource and audience on token exchange (Python) by @QuentinBisson in kagent-dev/kagent#2107 * Backport of 2407 (pg connection) to 0.10.x release by @supreme-gg-gg in kagent-dev/kagent#2399 * Backport 2409 (db session cleanup) to 0.10.x release by @supreme-gg-gg in kagent-dev/kagent#2403 * Backport of 2408 (s3 skills) to 0.10.x release by @supreme-gg-gg in kagent-dev/kagent#2405 * feat: configurable refresh interval for tool server controllers by @supreme-gg-gg in kagent-dev/kagent#2480 * feat: support file upload in agent chat by @supreme-gg-gg in kagent-dev/kagent#2429 ### Bug Fixes * fix(ui): chat interface overflows viewport causing input to be hidden below fold by @onematchfox in kagent-dev/kagent#2081 * fix(ui): avoid false stale chat send guard by @erauner12 in kagent-dev/kagent#2034 * fix(go-adk): extract system prompt from genai.GenerateContentConfig in Ollama and Bedrock model providers by @supreme-gg-gg in kagent-dev/kagent#2118 * fix(helm): support omitting image.registry in grafana-mcp chart by @michaelspinks in kagent-dev/kagent#2125 * fix(helm): support omitting image.registry in querydoc chart by @michaelspinks in kagent-dev/kagent#2124 * fix(security): patch CRITICAL/HIGH CVEs in golang-adk-full and app images by @jsonmp-k8 in kagent-dev/kagent#2133 * fix(go-adk): openai embeddings generation by @supreme-gg-gg in kagent-dev/kagent#2132 * fix(helm): omit empty repository segment in bundled image path by @michaelspinks in kagent-dev/kagent#2120 * fix: intermittent Postgres deadlock for concurrent memory searches by @supreme-gg-gg in kagent-dev/kagent#2146 * fix(deps): bump google-adk to 1.32.0 for Anthropic thinking blocks by @anxkhn in kagent-dev/kagent#2172 * fix: fix typos in part_converter comments by @mesutoezdil in kagent-dev/kagent#2195 * fix(adk): annotate serialize_metadata_value as str | dict by @anxkhn in kagent-dev/kagent#2173 * fix(ui): fix model name editing on the model config form by @Valyrian-Code in kagent-dev/kagent#1979 * fix: correct package comment in v1alpha2 groupversion_info by @mesutoezdil in kagent-dev/kagent#2183 * fix(crewai): unwrap Part before DataPart check so structured inputs are used by @anxkhn in kagent-dev/kagent#2231 * fix(helm): expose nodeSelector and tolerations for kagent-tools subchart by @the-mentor in kagent-dev/kagent#2213 * fix(helm): make the cluster DNS domain configurable by @shekhargit1912 in kagent-dev/kagent#2209 * fix: parse migration filenames the same way golang-migrate does by @mesutoezdil in kagent-dev/kagent#2194 * fix: build sandbox runtime outside QEMU emulation by @peterj in kagent-dev/kagent#2299 * fix: set kgateway.dev/a2a as appProtoco for BYO agents by @supreme-gg-gg in kagent-dev/kagent#2305 * fix(db): normalize agent name in memory vector search by @mesutoezdil in kagent-dev/kagent#2307 * fix(adk/bedrock): coerce nil tool-call args to empty object by @go-faustino in kagent-dev/kagent#2222 * fix: correct webhook typo in CLI help text by @tziyon31 in kagent-dev/kagent#2203 * fix(build): use AZURE_OPENAI_API_KEY env var name consistently by @mesutoezdil in kagent-dev/kagent#2310 * fix(security): scope task get, create, and delete to the owner by @mesutoezdil in kagent-dev/kagent#2265 * fix(adk): keep token refresh loop alive after a failed read by @mesutoezdil in kagent-dev/kagent#2330 * fix(acp-shim): drain child stdout during terminate to avoid teardown deadlock by @tomsen02 in kagent-dev/kagent#2338 * fix(adk): keep full session state when num_recent_events is set by @mesutoezdil in kagent-dev/kagent#2329 * fix(controller): wire up SessionSharesHandler in NewHandlers (#2363) by @onematchfox in kagent-dev/kagent#2420 * fix: ensure API key passthrough works for OpenAI embedding models by @onematchfox in kagent-dev/kagent#2494 * fix(adk): validate ask_user questions by @erauner12 in kagent-dev/kagent#2486 * Backport fix(golang-adk): resolve user identity from A2A call context to 0.10.x by @buravc in kagent-dev/kagent#2507 * fix(a2a): serve exact task reads from the persistent store by @erauner12 in kagent-dev/kagent#2484 * fix(ui): preserve post-login redirect target through `oauth2-proxy` sign-in by @onematchfox in kagent-dev/kagent#2532 ### Documentation * docs: fix duplicate descriptions and broken link in README by @mesutoezdil in kagent-dev/kagent#2068 * docs: fix typos and errors in component READMEs by @mesutoezdil in kagent-dev/kagent#2070 * docs: fix typos and list formatting in contributor docs by @mesutoezdil in kagent-dev/kagent#2069 * docs: replace outdated AutoGen reference in SECURITY.md by @mesutoezdil in kagent-dev/kagent#2110 * docs(python): fix kagent-langgraph README import to kagent.langgraph by @anxkhn in kagent-dev/kagent#2167 * docs(python): fix KAgentSession import path in kagent-openai README by @anxkhn in kagent-dev/kagent#2163 * docs: fix stale references and See merge request: Harmonybrew/homebrew-core!18335
Design choices are documented in #1933. Will address/respond to comments either here or there if needed and keep the implementation in sync as we go.
For testing locally, I first installed locally and started a few session as the default user
[email protected], I then reinstalled with trusted proxy enabled whereby I could test as my real user.Closes #1933