diff --git a/apps/web/src/action-command-dispatch-race.test.tsx b/apps/web/src/action-command-dispatch-race.test.tsx index 55a7f45d7..e20fd447a 100644 --- a/apps/web/src/action-command-dispatch-race.test.tsx +++ b/apps/web/src/action-command-dispatch-race.test.tsx @@ -1,14 +1,6 @@ -// Regression test for the off-route create-dialog race: firing a palette -// action command ("New skill") from a page other than its target route used -// to dispatch the shared "create" event synchronously, then navigate — but -// the target page's window-event listener only registers once it mounts, -// which happens on the next render after navigate's setState. The event -// fired and was gone before anyone was listening. -// -// runActionCommand now goes through a pending-flag (pending-dialog-request.ts, -// the same pattern library-upload.ts already used for "Upload artifact"): -// off-route, it records the flag and navigates; the target page consumes it -// on mount instead of relying on a same-tick dispatch. +// Regression: firing a palette action off-route used to dispatch a "create" +// event before the target page's listener mounted. Now it goes through a +// pending-flag (pending-dialog-request.ts) the target page consumes on mount. import { afterEach, describe, expect, test } from "bun:test"; import { act } from "react"; @@ -50,11 +42,8 @@ describe("runActionCommand off-route dispatch ordering", () => { }); expect(navigated).toEqual(["/skills"]); - // Only now (mirroring main.tsx's setState-based navigate re-rendering - // the route switch on the next tick) does the section actually mount. - // The section reads the stock skill-asset list on mount; serve it an - // empty one so the test exercises the pending-flag path, not a - // network failure. + // Serve an empty skill-asset list so the test exercises the + // pending-flag path, not a network failure. globalThis.fetch = (async () => new Response(JSON.stringify([]), { status: 200, diff --git a/apps/web/src/activatable-row.test.ts b/apps/web/src/activatable-row.test.ts index 54bcbadcc..8e64967ac 100644 --- a/apps/web/src/activatable-row.test.ts +++ b/apps/web/src/activatable-row.test.ts @@ -2,11 +2,8 @@ import { afterEach, describe, expect, test } from "bun:test"; import { isAdditiveSelectClick, isRowActivationKey, rowActivationProps } from "./activatable-row"; -// `isAdditiveSelectClick`'s Mac/non-Mac branch reads `navigator.platform`, -// which happy-dom's `GlobalRegistrator` reports as whatever the *host* OS -// is — Darwin-flavored on a Mac, something else on Linux CI. Each test -// below pins the platform it means to exercise instead of inheriting the -// host's, so both branches are deterministic on any OS. +// happy-dom reports `navigator.platform` as the host OS, so each test pins +// the platform it means to exercise to stay deterministic on any OS. const originalPlatform = Object.getOwnPropertyDescriptor(navigator, "platform"); function stubPlatform(platform: string): void { diff --git a/apps/web/src/activatable-row.ts b/apps/web/src/activatable-row.ts index 420618714..3c2f2802f 100644 --- a/apps/web/src/activatable-row.ts +++ b/apps/web/src/activatable-row.ts @@ -1,22 +1,13 @@ -// A clickable `TableRow` (library, agents, skills) needs the same three -// things to be keyboard-operable: an accessible role, a tab stop, and -// Enter/Space activation. Pure so the "which keys activate a row" rule is -// testable without rendering a table. +// Pure so "which keys activate a row" is testable without a table render. import type { KeyboardEvent } from "react"; export function isRowActivationKey(key: string): boolean { return key === "Enter" || key === " "; } -/** - * Whether a click's modifiers mean "add this row to the selection" rather - * than "activate/replace". Cmd-click is the additive gesture on every - * platform; Ctrl-click only joins in on non-Mac, because on Mac Ctrl-click - * is the native context-menu gesture — the browser can fire `click` and - * `contextmenu` from the same physical click, and treating Ctrl as additive - * there would silently toggle the very row the context menu is about to - * act on. - */ +// Ctrl-click is additive only on non-Mac; on Mac it's the context-menu +// gesture, and treating it as additive there would toggle the row the +// context menu is about to act on. export function isAdditiveSelectClick(event: { readonly metaKey: boolean; readonly ctrlKey: boolean; diff --git a/apps/web/src/agent-deploy.ts b/apps/web/src/agent-deploy.ts index fb55a3cfa..afdc20ade 100644 --- a/apps/web/src/agent-deploy.ts +++ b/apps/web/src/agent-deploy.ts @@ -1,9 +1,5 @@ // Deploys a hand-authored agent the same way Myra deploys herself -// (`myra-deploy.ts`): a `workflow`-kind asset holding a rendered source -// tree, pushed over the stock git smart-HTTP route, then deployed through -// the stock `POST /workflows/deployments`. Generalized over {name, -// displayName, systemPrompt} so the create-agent panel can deploy any -// agent through the one path the platform actually backs. +// (`myra-deploy.ts`), generalized over {name, displayName, systemPrompt}. import { renderBundledWorkflowSourceTree } from "@corbits/workflows/client"; import { type } from "arktype"; import { WorkflowDeploymentResponse } from "@intx/types"; @@ -156,10 +152,8 @@ async function withPushToken( } } -/** Renders this agent's built definition as the same bundled source tree - * Myra deploys (`pushMyraSource`) — the bundle's `buildMyraWorkflow` is - * generic over which agent it builds — and pushes it to the asset's `main`. - * Returns the commit sha the deploy pins to. */ +// The bundle's `buildMyraWorkflow` is generic over which agent it builds. +// Returns the commit sha the deploy pins to. export async function pushAgentSource( tenantId: string, assetId: string, @@ -219,11 +213,8 @@ export function isAgentDeploySourceAssetName(name: string): boolean { return AGENT_DEPLOY_SOURCE_ASSET_NAME.test(name); } -/** The inverse of `agentDeploySourceAssetName`: recovers the slug this - * pipeline deployed an asset under, so a caller re-deploying an existing - * agent can reuse its slug instead of re-deriving one from its display - * name. Null when the name isn't this pipeline's `agent--source` - * shape. */ +/** Lets a caller re-deploying an existing agent reuse its slug instead of + * re-deriving one from its display name. */ export function agentSlugFromSourceAssetName(assetName: string): string | null { const match = AGENT_DEPLOY_SOURCE_ASSET_NAME.exec(assetName); return match?.[1] ?? null; @@ -257,12 +248,8 @@ export function buildScheduledRunBody(deployerAddress?: string): string { return `This is your scheduled run. ${task} Mail the result to ${deployerAddress} (pass it as a single-item \`to\` list) with a short, descriptive subject.`; } -/** The deploying person's mailbox address — same source `session.ts`'s - * `fetchSession` reads, same shape `threads-api.ts` builds a person - * participant's address from (`@`). Best-effort: a - * session probe that fails or comes back signed-out just means the - * scheduled run's body falls back to naming nobody, never a failed - * deploy over it. */ +// Best-effort: a failed or signed-out session probe just means the +// scheduled run's body falls back to naming nobody, never a failed deploy. async function resolveDeployerAddress( tenantDomain: string, fetchImpl: typeof fetch, @@ -316,13 +303,8 @@ async function scheduleAgentRun( export type DeployedAgent = typeof WorkflowDeploymentResponse.infer; -/** - * Deploys a hand-authored agent: ensures its source asset, pushes its - * rendered definition, resolves the tenant's existing inference offering - * (the same one Myra's own deploy resolves through), and deploys through - * the stock `POST /workflows/deployments`. Fails closed when no provider is - * connected yet — there is no offering to deploy against. - */ +// Fails closed when no provider is connected yet — there is no offering +// to deploy against. export async function deployAgentSource( args: { readonly tenantId: string; readonly input: NewAgentInput }, fetchImpl: typeof fetch = fetch, diff --git a/apps/web/src/agent-source-read.ts b/apps/web/src/agent-source-read.ts index 39698771c..76ebbe074 100644 --- a/apps/web/src/agent-source-read.ts +++ b/apps/web/src/agent-source-read.ts @@ -1,7 +1,4 @@ -// Reads an existing agent's deploy source back out of its workflow asset, -// so the new-workbench picker can re-push the same definition into a -// child tenant (agent-deploy.ts already generalizes that push+deploy). -// There is no stock file-read route for a workflow asset (only +// No stock file-read route exists for a workflow asset (only // package-registry tarballs get one), so this fetches `main` over the // asset's smart-HTTP git remote with a short-lived read-only token. import { @@ -44,10 +41,8 @@ export type AgentToolPackagePin = { readonly name: string; readonly version: str type AgentWorkflowStep = (typeof AgentWorkflowJsonShape.infer)["steps"][string]; -/** Mints a read-only token, fetches the asset's `main` over its smart-HTTP - * git remote, and parses `definition.json` out of it. Shared by every - * reader below so each mints and revokes its own short-lived token rather - * than holding one open across a batch of assets. */ +// Each reader mints and revokes its own short-lived token rather than +// holding one open across a batch of assets. async function readAgentWorkflowStep( tenantId: string, assetId: string, @@ -101,11 +96,8 @@ export async function readAgentSource( }; } -/** The tool packages an agent's own step pins in `definition.json`. Empty - * for an agent whose tools ride bundled into its `workflow.js` closure - * instead (Myra's mail/posix factories never surface here — see - * `deployed-tool-packages.ts`'s `MYRA_TOOL_PACKAGES`, derived from - * `@corbits/myra/package.json`'s own dependencies). */ +/** Empty for an agent whose tools ride bundled into its `workflow.js` + * closure instead — see `deployed-tool-packages.ts`'s `MYRA_TOOL_PACKAGES`. */ export async function readAgentToolPackagePins( tenantId: string, assetId: string, diff --git a/apps/web/src/agents-api.ts b/apps/web/src/agents-api.ts index a31c4cdba..f47b22b7a 100644 --- a/apps/web/src/agents-api.ts +++ b/apps/web/src/agents-api.ts @@ -1,9 +1,5 @@ -// The hub seams this app still needs for agents: definitions (for the -// sidebar's own-agent-DM listing in `shell/bench-activity.ts`), top-level -// runs (for `chat/threads-api.ts`'s live-address resolution), and the -// create-agent deploy mutation. The Agents page itself now reads through -// `chat/threads-api.ts`'s `listChatAgents` — the same chat-partner listing -// — so this file no longer carries a roster/detail data model of its own. +// The Agents page reads through `chat/threads-api.ts`'s `listChatAgents`; +// this file carries no roster/detail data model of its own. import { WorkflowDefinitionResponse, WorkflowRunResponse, paginatedSchema } from "@intx/types"; import { type } from "arktype"; @@ -21,10 +17,8 @@ export type AgentInstance = typeof WorkflowRunResponse.infer; const DefinitionsPage = paginatedSchema(WorkflowDefinitionResponse); const InstancesPage = paginatedSchema(WorkflowRunResponse); -// The REST pagination ceiling (see `vendor/intx/hub-api/src/pagination.ts`). -// A bench with more agents or instances than this needs real pagination on -// this page, not raised here — tracked as a known limit, not silently -// worked around. +// The REST pagination ceiling; a bench past this needs real pagination, +// not a raised limit here. const PAGE_LIMIT = 100; type Validator = (data: unknown) => T | ArkErrors; @@ -60,28 +54,16 @@ export function listAgentDefinitions(tenantId: string): Promise page.data); } -/** - * The tenant's genuine top-level deployment runs — every non-top-level - * run (workbench host, invited agent, task) excluded server-side by the - * native `GET /workflows/runs` listing's own predicate (`address IS NOT - * NULL AND anchorRunId = id`), the same one `isTopLevelRun` uses. - * Single-tenant (accepted loss): the deleted route expanded the - * requested tenant to its whole descendant subtree via - * `getDescendantTenants`; the native listing filters one tenant, so a - * workspace parent sees only its own runs, not its child workbenches'. - */ +// Single-tenant (accepted loss): the native listing filters one tenant, so +// a workspace parent sees only its own runs, not its child workbenches'. export function listTopLevelRuns(tenantId: string): Promise { return getJSON(`/api/tenants/${tenantId}/workflows/runs?limit=${PAGE_LIMIT}`, InstancesPage).then( (page) => page.data, ); } -/** - * Deploys a hand-authored agent through the stock workflow-deploy path - * (`agent-deploy.ts`), then invalidates the bench's agent directory and - * chat-agent roster so both pick up the new deployment without a manual - * refetch. - */ +// Invalidates the bench's agent directory and chat-agent roster so both +// pick up the new deployment without a manual refetch. export function useDeployAgentMutation(tenantId: string) { const queryClient = useQueryClient(); return useMutation({ diff --git a/apps/web/src/api.ts b/apps/web/src/api.ts index 328a326e6..5a506a4cb 100644 --- a/apps/web/src/api.ts +++ b/apps/web/src/api.ts @@ -1,7 +1,5 @@ -// The interface's one seam to the hub: relative /api paths on the origin the -// bundle was served from, validated at the boundary with the platform's own -// response schemas so a shape change surfaces as an error state, never as -// undefined leaking into a page. +// Every response is validated at the boundary with the platform's own +// schemas, so a shape change surfaces as an error state, never `undefined`. import { ApprovalResponse, @@ -25,11 +23,8 @@ export const PrincipalsSchema = paginatedSchema(PrincipalSummary); export const TenantApprovalsSchema = paginatedSchema(ApprovalResponse); export const TenantDetailSchema = TenantResponse; -// `GET /api/tenants/:tenantId/assets` returns a bare array of -// `AssetWithOriginResponse` rows (not the paginated envelope), so the schema -// validates the array directly. These tenant assets — workflows, skills, -// package registries, agent state — are the real, listable store the Library -// page renders as artifacts. +// Returns a bare array, not the paginated envelope, so this validates the +// array directly. export const AssetsSchema = AssetWithOriginResponse.array(); // Real Library plane: paginated list from GET /api/tenants/:id/artifacts. @@ -83,26 +78,17 @@ export type ArtifactListItem = typeof ArtifactListItemSchema.infer; export type ArtifactListPage = typeof ArtifactListPageSchema.infer; export type ArtifactDetail = typeof ArtifactRowSchema.infer; export type ArtifactCounts = typeof ArtifactCountsSchema.infer; -/** - * The envelope paginatedSchema validates, stated structurally: the generic - * schema's inferred type carries an arktype inference artifact that rejects - * plain literals, so pages and tests use this equivalent shape instead. - */ +// Stated structurally because the generic schema's inferred type rejects +// plain literals. type Paginated = { data: T[]; nextCursor: string | null }; export type PrincipalsPage = Paginated; /** An arktype schema, seen as the validating call every `Type` provides. */ type Validator = (data: unknown) => T | ArkErrors; -/** - * Fetches one hub endpoint and reports exactly what happened: loading, no - * session (401), a failure, or validated data. Pass a module-level schema so - * identity stays stable; the schema never enters the query key. - * - * Empty paths are disabled and never fetch — the boundary owns the gate so - * call sites that still pass `""` when a tenant is unresolved cannot hit - * the network with a broken URL. - */ +// Pass a module-level schema so identity stays stable. Empty paths are +// disabled and never fetch, so a call site with an unresolved tenant +// can't hit the network with a broken URL. export function useAPIQuery( path: string, schema: Validator, @@ -133,11 +119,7 @@ export function useAPIQuery( return toAPIQuery(result); } -/** - * A one-shot POST against a hub route, parsed the same way `useAPIQuery` - * parses its GETs: loud on a non-2xx status and on a response shape that - * doesn't match the schema, never a silent fallback. - */ +// Loud on a non-2xx status or a shape mismatch, never a silent fallback. async function postJSON(path: string, schema: Validator, body: unknown): Promise { let response: Response; try { @@ -184,13 +166,8 @@ export function rejectApproval( ); } -/** - * One-shot fetch of `GET /api/tenants/:id` — the only place `parentId` - * comes from. A bench is a top-level tenant (`parentId === null`); a workbench - * is a named child tenant, so the raw-id/name heuristic can never tell them - * apart. `bench-context.tsx` fans this out per membership with - * `useQueries` to decide which memberships are benches. - */ +// The only place `parentId` comes from — a bench is a top-level tenant, +// so a raw-id/name heuristic can never tell it apart from a workbench. export async function fetchTenantDetail(tenantId: string): Promise { const response = await fetch(`/api/tenants/${encodeURIComponent(tenantId)}`, { headers: { accept: "application/json" }, @@ -209,24 +186,13 @@ export async function fetchTenantDetail(tenantId: string): Promise return parsed; } -/** - * Sandboxed HTML preview URL for a Library artifact — the same - * path an `