feat(ui): add full public API management surfaces - #359
Sertaç Özercan (sozercan) wants to merge 34 commits into
Conversation
Rebuild the ACP v2 change set linearly on the current main branch while preserving the pull request effective tree. Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Rebuild the integrated OpenCode runtime change set linearly on the rewritten ACP v2 base. Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
* docs(harness): record the v1/v2 coexistence plan and phase-0 ADRs Vendor the full coexistence specification (Revision 4) and record the Phase 0 decisions: ADR 0016 selects full active coexistence over blue/green replacement and the zero-active-state bridge, and ADR 0017 fixes the architecture and ownership contracts the implementation is built against. Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(api): add the harness v1/v2 coexistence bridge schema and contract gates Phase 1 of the coexistence plan: - Add AgentExecutionControl (durable revisioned backend modes and ownership fences), AgentExecutionPolicy (admin-owned v1 compatibility policy with structural fail-closed floors), and AgentExecutionAdjudication (immutable, fenced operator resolution). - Make AgentRuntime a discriminated dual-contract union: both protocol enum values, immutable optional contractVersion with no default, restored v1 bearer auth and capability fields as optional pointers, and discriminator CEL that rejects mixed shapes. Restore the v1 observedAuthRefResourceVersion status surface. - Add the immutable spec.runtime.contractVersion selector to built-in Agents; a missing selector is never protocol evidence and ACP planning, dispatch routing, and the v2 OpenCode validator now fail closed on anything but an explicit orka.harness.v2 classification. - Add write-once immutable Task status surfaces: agentExecutionBinding, agentExecutionNoExecution, agentExecutionQuarantine, and agentExecutionResolutionRef, with exclusivity and cross-protocol freeze CEL. Restore status.harnessRuntime and the legacy spec.agentRuntime.workspace shape as ratcheted compatibility-read surfaces that new Tasks can never introduce. - Prove the bridge on a real cluster: scripts/coexistence-bridge-crd- validate.sh exercises 34 live acceptance/rejection cases covering both-baseline round-trips, mixed-shape rejection, immutability, the closing-barrier status rules, and policy floors. - Stamp samples, demo, and e2e Agent manifests with the explicit v2 selector and mirror the new surfaces in the UI schemas. Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(store): add coexistence snapshot, lineage, v1 attempt, and ledger stores Durable payload foundation for the coexistence release; Kubernetes control CRDs and coordination Leases remain authoritative: - Immutable content-addressed execution snapshots, AES-256-GCM encrypted at rest with task/digest-bound additional data; persistence fails closed until a cipher is configured, and identical persists are idempotent while same-key divergence is a duplicate mismatch. - Session protocol/runtime lineage with an atomic claim: concurrent first use converges on exactly one lineage, cross-protocol continuation and recreated same-name Session or namespace UIDs fail with conflicts, and unclaimed pre-existing Sessions are never silently established. - The durable harness v1 attempt aggregate with the full Prepared/Submitting/SubmittedUnknown/CancelRequested state machine, fenced version CAS, operation-digest idempotency, and an OutcomeUnknown-requires-reason rule. - The wrapper admission ledger as its own SQLite database: idempotent turn admission, digest-mismatch permanent rejection, durable non-acceptance proof, terminal and OutcomeUnknown receipts that survive restart, and a durable admission-close marker that fails new admissions closed while pre-close turns still settle. Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(controller): freeze a write-once execution binding before ACP dispatch Implement the coexistence plan's phase-3 binding stage for the v2 path: - resolveAgentExecutionCandidate performs pure resolution of the frozen session configuration, runtime plan, namespace identity, and durable backend admission mode into a canonical snapshot body and a digest-stable prospective binding. When the AgentExecutionControl singleton exists its v2 backend must be effectively enabled, and the admitted mode revision freezes into the binding; a drain-only or disabled backend rejects new bindings and never falls back. - persistAgentExecutionSnapshot idempotently stores the immutable encrypted snapshot before any binding write. - persistAgentExecutionBinding is an uncached compare-if-absent CAS: a UID change or deletion never dispatches, an identical candidate replays idempotently, and a mismatched candidate is a permanent BindingConflict that never overwrites the original binding. - verifyBoundExecution re-reads the Task and backend control uncached immediately before the first executor side effect and blocks dispatch on digest drift, deletion, mode closure, or control recreation. The stage activates when --agent-execution-snapshot-key-file supplies the AES-256 snapshot key; without it the controller logs that the coexistence release requires the key and keeps the legacy direct-queue path so pre-coexistence deployments are not bricked. Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(controller): claim session protocol lineage atomically with the lease Wire the coexistence plan's session-lineage invariant into the v2 path: ACPSessionContinuity.AcquireMutationLease now establishes or verifies the Session's protocol/runtime lineage under the just-acquired mutation lease, so concurrent first-use Tasks serialize on the lease and converge on exactly one lineage. A lineage conflict (cross-protocol continuation, a different runtime identity, or a recreated same-name Session or namespace UID) releases the lease and fails the acquisition instead of silently reusing opposite runtime state, and never edits the original lineage. On this pure-v2 line a pre-existing transcript without a lineage row is adopted as orka.harness.v2 evidence; the dual-controller sealed-inventory classification replaces that adoption rule. The dispatcher resolves the namespace UID uncached and threads the built-in runtime identity and reserved profile digest into lease acquisition; production wiring always records lineage. Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(harness): add v1/v2 coexistence bridge Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(harness): complete coexistence rollout safeguards Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden coexistence recovery paths Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): restore coexistence validation Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): keep singleton helm controller Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): close coexistence safety gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden coexistence closeout Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden harness v1 dispatch and admission Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: remove invalid admission readiness probe Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: send admission smoke requests as JSON Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: address coexistence lifecycle review gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: close coexistence migration gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): seed execution control in substrate e2e Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): tighten v1 compatibility admission Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): close coexistence safety gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden coexistence upgrades Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): close coexistence review gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): align harness secret assertion Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden cleanup and v1 admission Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat(harness): complete v1 and v2 coexistence Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): allow idempotent policy retries Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(admission): allow post-DNAT API egress Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden wrapper cleanup and recovery Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): close v1 admission gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): unblock first-use session dispatch Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden credential safety Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): include harness helpers in manager builds Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(build): run manager package Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): bound cancellation and git cleanup Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden coexistence retirement gates Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): repair coexistence jq filters Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(acp): recover incomplete session admission Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden coexistence admission and redaction Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): enable admission for harness render Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden harness coexistence paths Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: address coexistence review findings Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * feat: add isolated harness controller modes Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): preserve static mode validator executable Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): validate static Helm mode fixtures Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): validate rendered token request RBAC Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): reject wildcard token request grants Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * refactor: simplify harness controller modes Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden harness v1 runtime validation Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: scope static controller maintenance reads Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: acknowledge harness v1 settlement before deletion Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden harness v1 isolation edge cases Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: close harness v1 recovery gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(helm): reject controller watch scope changes Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): create substrate namespace before secrets Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: avoid cached namespace reads Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): configure substrate snapshot store Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): enforce isolated mode credentials Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden isolated controller ownership Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): provide harness v1 TLS fixture Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: close isolated controller validation gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ci): colocate substrate MCP template Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: address harness isolation review findings Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: scope control store reads to watch namespace Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(harness): harden static mode isolation Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(deploy): bootstrap static namespace identity Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: address isolated mode review findings Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden isolated harness installation boundaries Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: align admission trust and e2e checks Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(e2e): assert admission denial output Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden isolated harness deployment Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: close isolated harness review gaps Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: preserve snapshot encryption identity Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: harden isolated harness mode handling Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix(ui): clarify unclassified runtime state Signed-off-by: Sertac Ozercan <sozercan@gmail.com> * fix: complete isolated harness integrations Signed-off-by: Sertac Ozercan <sozercan@gmail.com> --------- Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
- New schemas: provider, skill, memory/proposal, whoami, substrate pool, gateway class, monitor workflow event; task spec gains cron/priorTaskRef/ requestedBy/transaction/clean-room workspace fields; agent spec gains contractVersion, fallbacks, autonomous coordination, ttlAfterLastTask - Dedupe execution-event schema (task.ts re-exports canonical shapes) - New hooks: providers/skills/memories/proposals/whoami CRUD+filters, substrate pools, agent-runtime registration, gateway classes, task plan/children, monitor update/events/patch-preview, security scan update/delete/slice detail, tool CRUD, agent update, chat cancel - MSW default handlers + hook tests for all new surface - Remove dead pr-create-dialog (endpoint no longer exists) and agent-grid-view (superseded by runtime roster)
- Providers: list/detail/create/edit/delete backed by /providers CRUD; base URL locked on edit to match server preserve-on-update behavior - Skills: list/detail (served markdown content)/create/edit/delete - Memory: governance-first page — memories browser with filters, enable/disable/soft-delete/edit, plus a proposal review inbox with accept/reject (+note), explicit apply-to-memory, and archive; 501 renders as a store-not-enabled state - System: /readyz checks, capability badges (chat, memory store), chat orchestrator limits/tools, compat endpoint connect info + model catalog - Header: free-text namespace switcher with history + whoami default; identity popover showing verified caller identity and transaction metadata; popover primitive added - Colocated tests for all of the above
- Runtimes: Substrate actor pools tab (list/create/edit/delete), external AgentRuntime registration + spec edit + remove via a shared YAML manifest editor; fail-closed dispatch note preserved - Gateways: cluster GatewayClass tab (contract, category, capabilities, acceptance) - New shared ManifestEditor (js-yaml) for deep specs — the kubectl-edit equivalent used by registration and upcoming edit flows
- Agents: creation now stamps runtime.contractVersion=orka.harness.v2 (empty was fail-closed at dispatch — UI-created runtime agents could never run); provider picker offers registered Provider CRs via providerRef alongside direct anthropic/openai; detail gains full-spec YAML editing - Monitors: spec editing, monitor event audit trail card, and implementation-job patch preview dialog (501/404-aware) - Security: scan config editing + repository delete with confirm - Tools: create from HTTP template, spec editing, delete - Chat: New Chat cancels + deletes the previous server-side session - ManifestEditor reworked to a guarded render-time reset (lint-clean)
…dashboard approvals - Task create: schedule/timeZone/concurrency/suspend, env, args, webhook, secretRef, retryPolicy, sessionRef, AI systemPrompt/temperature/maxTokens/ tools/skills + registered Provider refs, agent runtime overrides, clean-room workspace policies, and a seeded full-spec YAML mode - Task detail: requester + transaction provenance card, cron schedule card, API-resolved child task table alongside the execution graph - Dashboard: cross-task 'waiting for approval' inbox from task conditions - Sidebar: grouped water-column navigation (Operate / Automation / Registry / Fabric / Platform) covering the new sections - Docs: guides/ui.md page table refreshed; ui/README.md replaced with real documentation; test-perf fix (single-event fills) for the large form
Signed-off-by: Sertac Ozercan <sozercan@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2450967676
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "Codex (@codex) review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".
| items.map((provider) => ( | ||
| <TableRow key={provider.name}> | ||
| <TableCell> | ||
| <Link | ||
| to="/providers/$providerName" | ||
| params={{ providerName: provider.name }} |
There was a problem hiding this comment.
Normalize full Provider objects before rendering the list
For ordinary non-context-token authentication, ListProviders returns full Kubernetes Provider objects whose values live under metadata, spec, and status; only context-token responses use the flat shape assumed here. Consequently provider.name is undefined for normal UI users, producing unusable rows and links and also preventing the task form's Provider selector from working. Normalize both response shapes or render the full Provider shape.
Useful? React with 👍 / 👎.
| <PageHeader | ||
| eyebrow="Providers" | ||
| title={provider.metadata.name} | ||
| description={provider.status?.message} |
There was a problem hiding this comment.
Handle redacted Provider details for context-token callers
When the UI is authenticated with a context token, GetProvider deliberately returns a flat redacted item rather than a full Provider object. The list links context-token users to this page, but dereferencing provider.metadata.name then throws because metadata and spec are absent. Render the redacted response safely or avoid offering the full-detail route in this authentication context.
Useful? React with 👍 / 👎.
| export function useCancelChatSession() { | ||
| const queryClient = useQueryClient() | ||
| return useMutation({ | ||
| mutationFn: (sessionId: string) => api.delete<void>(`/chat/${sessionId}`), |
There was a problem hiding this comment.
Pass the selected namespace when cancelling chat
When a chat was created in any selected namespace other than the server's implicit default, this DELETE resolves a different namespace because it omits the namespace query parameter. Clicking New Chat therefore commonly gets a 404/403 while the abandoned turn keeps running and consuming chat concurrency; read the namespace from useUIStore and pass it as the other session mutations do.
Useful? React with 👍 / 👎.
| url: 'https://api.example.com/search', | ||
| method: 'POST', | ||
| timeout: '10s', | ||
| authSecretRef: { name: 'my-tool-credentials' }, |
There was a problem hiding this comment.
Remove protected authentication from the Tool starter
Submitting the New Tool dialog without manually deleting this field always fails: CreateTool calls validateToolRESTMutation, which rejects every REST request containing spec.http.authSecretRef and requires protected HTTP authentication to be configured through Kubernetes RBAC. Seed the REST editor with an allowed unauthenticated example or clearly route protected-tool creation through the Kubernetes API.
Useful? React with 👍 / 👎.
| limits: { maxResidentSessions: 10, maxConcurrentPrompts: 4 }, | ||
| workspaceGovernance: { mode: 'strict-governed' }, |
There was a problem hiding this comment.
Seed a valid v2 AgentRuntime registration manifest
The registration starter cannot pass the AgentRuntime CRD schema: the limits object supplies only two of eleven required positive fields, while the strict-governance object omits its required boolean guarantees; the profile also omits required digest/version, adapter, policy, proxy, and resource-class fields and starts required digest/model values invalid. Because the REST handler sends this manifest to Kubernetes, the default Register action is rejected unless users already know and add the undocumented fields.
Useful? React with 👍 / 👎.
| <div className="space-y-2"> | ||
| <label htmlFor="task-secret-ref" className="text-sm font-medium">Secret ref</label> | ||
| <Input id="task-secret-ref" value={secretRefName} onChange={(e) => setSecretRefName(e.target.value)} placeholder="task-credentials" /> |
There was a problem hiding this comment.
Hide the generic Secret ref for ACP agent Tasks
When an agent Task is selected, this common advanced field still writes top-level spec.secretRef, but built-in and external ACP runtime validation rejects credential SecretRefs, causing the newly created Task to fail instead of dispatching. Restrict this field to container/native-AI Tasks and keep agent repository credentials in the workspace credential-reference fields.
AGENTS.md reference: AGENTS.md:L87-L87
Useful? React with 👍 / 👎.
| if (schedule.trim()) { | ||
| body.schedule = schedule.trim() | ||
| if (timeZone.trim()) body.timeZone = timeZone.trim() | ||
| if (concurrencyPolicy) body.concurrencyPolicy = concurrencyPolicy |
There was a problem hiding this comment.
Reject invalid schedule time zones before submission
Any non-empty text entered here is submitted as timeZone, while both scheduled-task controller paths silently ignore time.LoadLocation errors and proceed in the process/default time zone. A typo such as America/Los_Angelesx therefore creates a seemingly valid schedule that runs at the wrong time without an error; validate that the value is a real IANA zone before creating the Task.
Useful? React with 👍 / 👎.
| toolCount={toolsData?.items?.length} | ||
| isLoading={isLoading} | ||
| /> | ||
| <NeedsAttention tasks={tasksData?.items ?? []} /> |
There was a problem hiding this comment.
Fetch all Tasks for the approval inbox
The dashboard requests only one 100-item Task page and passes that page to the new cross-task approval inbox. In namespaces with more than 100 Tasks, any WaitingForApproval=True Task beyond the first API page is silently omitted, so operators can miss work requiring intervention; drive this inbox from useTaskListAll or paginate specifically for waiting Tasks.
Useful? React with 👍 / 👎.
| // Both fields are immutable and rejected on client writes, so everything | ||
| // here is verified audit data. | ||
| export function TaskIdentityCard({ task }: { task: Task }) { | ||
| const requestedBy = task.spec.requestedBy |
There was a problem hiding this comment.
Do not label Kubernetes-supplied identity as verified
These fields are server-stamped for REST-created Tasks, but Task CRDs created directly through the Kubernetes API can set arbitrary spec.requestedBy and spec.transaction values on creation because the transition rules only make them immutable afterward and no admission webhook rejects them. Presenting every stored value as verified audit data lets a Kubernetes writer spoof the identity shown here; distinguish REST-stamped provenance or enforce these fields at Kubernetes admission.
Useful? React with 👍 / 👎.
| return useQuery({ | ||
| queryKey: ['skills', namespace], | ||
| queryFn: () => api.get<ListResponse<SkillListItem>>('/skills', { namespace }), | ||
| refetchInterval, |
There was a problem hiding this comment.
GET /skills defaults to a 100-item page and returns further pages through metadata.continue, but this hook fetches only the first page and the new list exposes no pagination control. Namespaces with more than 100 Skills therefore show an incomplete inventory and make later Skills unreachable from the management surface; follow continuation tokens as the runtime hooks do or add list pagination.
Useful? React with 👍 / 👎.
Summary
Dependency
acpbranchTesting
cd ui && bun install --frozen-lockfilecd ui && bun run lintcd ui && bun run test(145 files, 1018 tests)