ConusAI vendor v0.1.0-beta.114
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-beta.114
2026-11-14 09:56:45 +00:00
commit 1ce8d23c35
867 changed files with 122104 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
"use client"
// The approval queue: what a run's tool asked a person to decide. The panel renders the EXACT
// stored proposed arguments — a pretty-printed JSON of what the tool was called with — never a
// model's narration of them, and it carries the AI disclosure a reviewer reads before deciding.
//
// The queue is live: the page's view hands over the approvals it rendered as the seed, and this reads them
// again whenever the live stream announces a change of a run (`agent.run`) -- a run that parks an approval
// or leaves one changes its status -- and after this person's own decision, so nothing reloads the page.
import { AiDisclosureBadge, type ProjectSlugs } from "@conusai-kit/ui"
import { describeError, useRead, useWrite } from "@conusai-kit/ui/data"
import { PageSection, PageState } from "@conusai-kit/ui/patterns"
import { Button, ItemActions } from "@conusai-kit/ui/primitives"
import { type ReactElement, useState } from "react"
import { type ApprovalView, RUN_COLLECTION } from "../contract/index.js"
export interface ApprovalQueueLabels {
readonly heading: string
readonly empty: string
readonly proposed: string
readonly review: string
readonly approve: string
readonly reject: string
readonly decided: string
readonly disclosure: string
readonly failed: string
}
export function ApprovalQueue({
slugs,
seed,
labels,
}: {
slugs: ProjectSlugs
/** The approvals the server rendered: shown at once, then kept current. */
seed: readonly ApprovalView[]
labels: ApprovalQueueLabels
}): ReactElement {
const queue = useRead<{ approvals: readonly ApprovalView[] }>(
"agent.approval.list",
{ ...slugs },
{
live: {
org: slugs.org,
topic: { ws: slugs.workspace, project: slugs.project },
entities: [RUN_COLLECTION.entity],
},
initial: { approvals: seed },
},
)
const approvals = queue.data?.approvals ?? seed
const review = useWrite<ProjectSlugs & { runId: string; stepKey: string; decision: "approve" | "reject" }, unknown>(
"agent.approval.review",
)
const [busy, setBusy] = useState<string | null>(null)
const [message, setMessage] = useState<string | null>(null)
const decide = async (approval: ApprovalView, decision: "approve" | "reject"): Promise<void> => {
setBusy(`${approval.runId}:${approval.stepKey}`)
setMessage(null)
try {
await review.mutate({ ...slugs, runId: approval.runId, stepKey: approval.stepKey, decision })
setMessage(labels.decided)
// The decision is a step's, which the stream does not announce until the run moves: read it again now.
await queue.refetch()
} catch (error) {
setMessage(labels.failed.replace("{detail}", describeError(error)))
} finally {
setBusy(null)
}
}
return (
<PageSection title={labels.heading}>
{approvals.length === 0 ? <PageState title={labels.empty} /> : null}
{approvals.map((approval) => (
<PageSection
key={`${approval.runId}:${approval.stepKey}`}
title={approval.tool}
description={approval.goal}
level={3}
variant="card"
>
<AiDisclosureBadge label={labels.disclosure} />
<h4>{labels.proposed}</h4>
<pre>{JSON.stringify(approval.arguments, null, 2)}</pre>
<ItemActions>
<Button disabled={busy !== null} onClick={() => void decide(approval, "approve")}>
{labels.approve}
</Button>
<Button variant="outline" disabled={busy !== null} onClick={() => void decide(approval, "reject")}>
{labels.reject}
</Button>
{approval.reviewUrl !== null ? (
<Button asChild variant="link">
<a href={approval.reviewUrl} target="_blank" rel="noreferrer">
{labels.review}
</a>
</Button>
) : null}
</ItemActions>
</PageSection>
))}
{message !== null ? (
<p role="status" aria-live="polite">
{message}
</p>
) : null}
</PageSection>
)
}