Skip to content

Commit 4dbe7a7

Browse files
authored
Merge branch 'main' into test-e2e-grpc-not-set
2 parents a78c117 + 291b083 commit 4dbe7a7

710 files changed

Lines changed: 68591 additions & 67127 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yaml

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -358,33 +358,45 @@ jobs:
358358
uses: actions/setup-node@v7
359359
with:
360360
node-version-file: ui/.nvmrc
361-
cache: "npm"
362-
cache-dependency-path: ui/package-lock.json
363361

364-
# Honor the pinned npm from ui/package.json "packageManager" so npm ci
365-
# resolves the lock file with the same npm version it was generated with.
362+
# Before any cache step that shells out to yarn: the pinned version in
363+
# ui/package.json "packageManager" is Yarn 4, and without corepack the shim
364+
# on the runner is a different one that cannot read this lock file.
366365
- name: Enable Corepack
367366
run: corepack enable
368367

368+
- name: Cache Yarn downloads
369+
uses: actions/cache@v4
370+
with:
371+
path: ui/.yarn/cache
372+
key: yarn-${{ runner.os }}-${{ hashFiles('ui/yarn.lock') }}
373+
restore-keys: yarn-${{ runner.os }}-
374+
369375
- name: Install dependencies
370376
working-directory: ./ui
371-
run: npm ci
377+
run: yarn install --immutable
378+
379+
- name: Typecheck
380+
working-directory: ./ui
381+
run: yarn typecheck
372382

373383
- name: Run lint
374384
working-directory: ./ui
375-
run: npm run lint
385+
run: yarn lint
376386

377-
- name: Run unit tests (Jest)
387+
- name: Run unit tests
378388
working-directory: ./ui
379-
run: npm run test
389+
run: yarn test
380390

381-
- name: Install Playwright browser (Chromium)
391+
# Both engines the suite declares. Installing only one leaves that project
392+
# failing to launch, which reads as a broken app rather than a missing browser.
393+
- name: Install Playwright browsers
382394
working-directory: ./ui
383-
run: npx playwright install --with-deps chromium
395+
run: yarn playwright install --with-deps chromium firefox
384396

385-
- name: Run Storybook tests (Vitest + Playwright)
397+
- name: Run browser tests
386398
working-directory: ./ui
387-
run: npm run test:vitest
399+
run: yarn test:pw
388400

389401
# This job builds the Docker images for the controller, UI, ADKs, and CLI on arm64.
390402
build:

.github/workflows/ui-chromatic.yaml

Lines changed: 0 additions & 55 deletions
This file was deleted.

.github/workflows/ui-playwright.yaml

Lines changed: 0 additions & 119 deletions
This file was deleted.

CLAUDE.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,85 @@ Common commands:
108108
- Do not commit or push unless asked.
109109
- Keep PRs focused. Explain non-obvious invariants and operational tradeoffs, not line-by-line implementation details.
110110

111+
## The web interface (`ui/`)
112+
113+
A Vite single-page app. It is a static bundle served by nginx: there is no server
114+
process, so there are no server components, no server-side data fetching and no
115+
file-system routing.
116+
117+
**Stack:** Vite + React 19, TypeScript, antd 6 for components, Emotion for styling
118+
(the `css` prop, via `jsxImportSource`), SWR for reads, Yarn 4. React Router owns
119+
routing; there is no file-system routing and no server rendering.
120+
121+
### Commands
122+
123+
Run these from `ui/`:
124+
125+
| Task | Command |
126+
|------|---------|
127+
| Dev server | `yarn dev` |
128+
| Unit tests | `yarn test` |
129+
| End-to-end, no cluster needed | `yarn test:pw` (Chromium and Firefox) |
130+
| End-to-end against a real cluster | `yarn test:pw:live` |
131+
| Type check | `yarn typecheck` |
132+
| Lint | `yarn lint` |
133+
134+
Only lint **errors** gate a change; a handful of warnings are pre-existing.
135+
136+
`scripts/setup-cluster/setup-cluster.sh` builds a Kind cluster with kagent on it in one
137+
command, for work that needs a real backend.
138+
139+
### Settings reach the app at runtime, not at build time
140+
141+
Configuration is read from `window.environmentVariables`, which the container
142+
rewrites from its own environment on every start. So one image serves every
143+
deployment, and a setting is an operator's decision rather than something frozen
144+
into a build. Locally the same values come from `ui/.env` (git-ignored;
145+
`ui/.env.example` documents each one).
146+
147+
Two consequences worth knowing before touching that code:
148+
149+
- The script that supplies them is **synchronous** in `index.html`. Several modules
150+
read settings at import time, so anything awaited would be read before it arrived.
151+
- `import.meta.env` is for build-time flags only. A value that an operator should be
152+
able to change belongs in `window.environmentVariables`.
153+
154+
### Fixtures are opt-in
155+
156+
`ENABLE_MOCK_UI=true` serves the whole API from an in-browser mock (MSW) with no
157+
cluster at all, and `?mock=ok|empty|error|slow` picks which scenario the fixtures
158+
play. **It is off unless asked for**, in a dev server exactly as in a built image: a
159+
page that quietly serves fixtures when the backend is down looks healthy while
160+
showing data that was never real.
161+
162+
When mock mode is on it overrides every backend setting, and anything reporting who
163+
is signed in correctly reports nobody — there is no backend to have signed in to.
164+
165+
### Extension points
166+
167+
One `VendorExtensionConfig` contributes navigation entries and overrides, routes and
168+
route handles, slots, form fields, table columns, API overrides, providers, theme
169+
tokens, shell regions, branding, provider icons and agent links. Components read
170+
every colour, radius and font from those tokens, so overriding them restyles
171+
components an extension never touches. When adding a feature, check whether it
172+
belongs behind an extension point rather than as a branch inside a shared component.
173+
174+
The full guide is [ui/docs/vendor-extensions.md](ui/docs/vendor-extensions.md).
175+
176+
### Conventions specific to this codebase
177+
178+
- **Say when data is not real.** A page showing fixtures says so on the page. Never
179+
suppress an error because a mock flag is set — a broken backend must not render as
180+
healthy mock data.
181+
- **Normalise at the client boundary.** Go marshals a nil slice as JSON `null`, so
182+
any collection the controller has nothing for arrives as null. Fix it once where the
183+
response is parsed, not at each use.
184+
- **Fixtures must match the controller, not each other.** A fixture, a type and a
185+
test can agree perfectly and all three be wrong; that has happened here more than
186+
once and each time only a real cluster objected. Check the CRD.
187+
- **Prefer a smaller honest test suite** over a green one that proves nothing.
188+
Coverage debt belongs in `playwright/DEFERRED.md`, not in skipped specs.
189+
111190
## 9. References
112191

113192
- [STYLE.md](STYLE.md)

docs/plans/api-v2-execution-plan.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ MCP:
326326

327327
- Discover ready AgentInstances.
328328
- Invoke them only through the public gateway A2A path.
329+
- Expose durable A2A turns through the MCP Tasks extension, including polling,
330+
cancellation, and input-required continuation.
331+
- Keep synchronous `tools/call` fallback for clients without Tasks support.
332+
- Store no MCP-owned task or session state.
329333
- Never expose Actor or private MCP endpoints.
330334

331335
Content:

go/api/database/client.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ type Client interface {
9494
GetSessionShareByToken(ctx context.Context, token string) (*SessionShare, error)
9595
ListSessionSharesBySession(ctx context.Context, sessionID string) ([]SessionShare, error)
9696
DeleteSessionShare(ctx context.Context, token, sessionID, userID string) error
97-
RecordShareAccess(ctx context.Context, userID string, shareID int64) error
9897

9998
// Agent memory (vector search) methods
10099
StoreAgentMemory(ctx context.Context, memory *Memory) error
@@ -124,12 +123,19 @@ type Client interface {
124123
CreateAgentInstance(context.Context, *apiv1alpha1.AgentInstance, string) (*apiv1alpha1.AgentInstance, bool, error)
125124
ForkAgentInstance(context.Context, string, string, string, string, string) (*apiv1alpha1.AgentInstance, bool, error)
126125
GetAgentInstance(context.Context, string, string, string) (*apiv1alpha1.AgentInstance, error)
127-
ListAgentInstances(context.Context, string, string, bool, map[string]string, string, int) ([]*apiv1alpha1.AgentInstance, error)
126+
ListAgentInstances(context.Context, AgentInstanceQuery) ([]*apiv1alpha1.AgentInstance, error)
127+
// UpdateAgentInstanceName sets the instance's display name, scoped to its owner.
128+
// Takes namespace, id, owner and the new name.
129+
UpdateAgentInstanceName(context.Context, string, string, string, string) (*apiv1alpha1.AgentInstance, error)
128130
MarkAgentInstanceReady(context.Context, string, string) (*apiv1alpha1.AgentInstance, error)
129131
TransitionAgentInstance(context.Context, *apiv1alpha1.AgentInstance, apiv1alpha1.AgentInstanceState, apiv1alpha1.AgentInstanceOperation) (*apiv1alpha1.AgentInstance, error)
130132
DeleteAgentInstance(context.Context, string) error
131133
CreateAgentInstanceShare(context.Context, AgentInstanceShare) (*AgentInstanceShare, error)
132134
ListAgentInstanceShares(context.Context, string, string, string, string, int) ([]AgentInstanceShare, error)
135+
// GetAgentInstanceShareByTokenHash resolves a share token to its share and the
136+
// owner of the instance it grants access to. Takes the digest, because only the
137+
// digest is stored.
138+
GetAgentInstanceShareByTokenHash(context.Context, []byte) (*AgentInstanceShare, error)
133139
DeleteAgentInstanceShare(context.Context, string, string, string) error
134140
// CreateAgentInstanceTask reserves the instance's single active-task slot.
135141
CreateAgentInstanceTask(context.Context, string, []byte, *a2a.Task) (*a2a.Task, bool, error)

go/api/database/models.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,14 +270,38 @@ type RuntimeRevision struct {
270270
GoldenSnapshot string
271271
}
272272

273+
// AgentInstanceQuery narrows a page of AgentInstances. Zero values mean "do not
274+
// filter on this", so an empty query lists the caller's own instances in the
275+
// namespace.
276+
type AgentInstanceQuery struct {
277+
Namespace string
278+
UserID string
279+
AllUsers bool
280+
MatchLabels map[string]string
281+
// AgentTemplate and Harness name the agent whose conversations are wanted.
282+
// They are matched against the (AgentTemplate, Harness) pair the instance's
283+
// prepared revision was built from, not against its labels, so they select
284+
// instances stored before either field existed.
285+
AgentTemplate string
286+
Harness string
287+
AfterID string
288+
Limit int
289+
}
290+
273291
type AgentInstanceShare struct {
274292
ID string
275293
Namespace string
276294
InstanceID string
277-
Creator string
278295
Permission string
279296
TokenHash []byte
280297
CreatedAt time.Time
298+
// OwnerUserID is the user the shared AgentInstance belongs to.
299+
//
300+
// Populated only by the token lookup, which joins it in — that is what the
301+
// share grants. A visitor is authenticated as themselves and the token widens
302+
// what their account may reach to what the *owner* can see, so the instance
303+
// read has to run as the owner or it finds nothing.
304+
OwnerUserID string
281305
}
282306

283307
// AgentInstanceTaskSnapshot identifies the immutable Substrate snapshot at a

0 commit comments

Comments
 (0)