feat: wire real AgentOS execution

This commit is contained in:
-Puter
2026-07-28 16:52:49 +05:30
parent 4d9f6da41b
commit 4edd456b5b
17 changed files with 1090 additions and 308 deletions

View File

@@ -5,8 +5,10 @@ import {
} from "@code/ui/components/ai-elements/conversation";
import { Button } from "@code/ui/components/button";
import {
AlertTriangle,
ChevronRight,
Check,
FileCode2,
FolderGit2,
Hammer,
LoaderCircle,
@@ -15,6 +17,7 @@ import {
ImagePlus,
Play,
RotateCcw,
ScrollText,
Send,
Sparkles,
Settings,
@@ -36,6 +39,28 @@ import {
type SliceWork = NonNullable<ReturnType<typeof useSliceOne>["works"]>[number];
const EMPTY_WORKS: readonly SliceWork[] = [];
type SliceArtifact = NonNullable<
NonNullable<SliceWork["runs"][number]["artifacts"]>[number]
>;
interface ArtifactMetadata {
readonly changedFiles?: readonly string[];
}
const parseArtifactMetadata = (artifact: SliceArtifact): ArtifactMetadata => {
if (!artifact.metadataJson) {
return {};
}
try {
return JSON.parse(artifact.metadataJson) as ArtifactMetadata;
} catch {
return {};
}
};
const changedFilesFor = (artifact: SliceArtifact): readonly string[] =>
parseArtifactMetadata(artifact).changedFiles ?? [];
interface WorkCardProps {
readonly onSourceSelect: (rawText: string) => void;
readonly work: SliceWork;
@@ -96,6 +121,7 @@ const starterDesign = (work: SliceWork) => ({
const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
const [sourcesOpen, setSourcesOpen] = useState(false);
const [expanded, setExpanded] = useState(false);
const [logsOpen, setLogsOpen] = useState(false);
const sources = work.signals.flatMap((signal) => signal.sources);
const { definition } = work;
const { design } = work;
@@ -258,6 +284,22 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
<p className="font-semibold uppercase tracking-wide text-[#65713a]">
Build
</p>
{slice.operationError ? (
<p className="mt-2 flex items-start gap-1.5 border border-red-300 bg-red-50 px-2 py-1.5 text-red-800">
<AlertTriangle className="mt-0.5 size-3.5 shrink-0" />
<span className="min-w-0 flex-1 leading-5">
{slice.operationError.message}
</span>
<button
aria-label="Dismiss error"
className="shrink-0"
onClick={() => slice.clearOperationError()}
type="button"
>
<X className="size-3.5" />
</button>
</p>
) : null}
{latestRun ? (
<div className="mt-1 space-y-2 text-[#626057]">
<p className="leading-5">
@@ -269,31 +311,75 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
latestRun.terminalClassification ??
"activity is still arriving"}
</p>
{latestRun.attemptEvents?.slice(-5).map((item) => (
<p
className="border-l-2 border-[#b8c760] pl-2"
key={item._id}
>
{item.message}
</p>
))}
{latestRun.baseRevision ? (
<p className="font-mono text-[10px] text-[#747168]">
{latestRun.baseRevision.slice(0, 8)} {" "}
{latestRun.candidateRevision?.slice(0, 8) ?? "working"}
</p>
) : null}
{latestRun.artifacts?.map((artifact) => (
<a
className="block font-medium underline"
href={artifact.uri}
key={artifact._id}
rel="noreferrer"
target="_blank"
>
{artifact.title}
</a>
))}
{latestRun.artifacts?.map((artifact) => {
const changedFiles = changedFilesFor(artifact);
return (
<div key={artifact._id} className="space-y-1">
{artifact.uri ? (
<a
className="block font-medium underline"
href={artifact.uri}
rel="noreferrer"
target="_blank"
>
{artifact.title}
</a>
) : (
<p className="font-medium">{artifact.title}</p>
)}
{changedFiles.length > 0 ? (
<ul className="space-y-0.5">
{changedFiles.map((file) => (
<li
className="flex items-start gap-1.5 font-mono text-[11px] leading-5 text-[#747168]"
key={file}
>
<FileCode2 className="mt-0.5 size-3 shrink-0 text-[#9a985f]" />
<span className="min-w-0 break-all">{file}</span>
</li>
))}
</ul>
) : null}
</div>
);
})}
{latestRun.attemptEvents &&
latestRun.attemptEvents.length > 0 ? (
<div className="space-y-1">
{(logsOpen
? latestRun.attemptEvents
: latestRun.attemptEvents.slice(-3)
).map((item) => (
<p
className="border-l-2 border-[#b8c760] pl-2 leading-5"
key={item._id}
>
{item.message}
</p>
))}
{latestRun.attemptEvents.length > 3 ? (
<button
className="flex items-center gap-1 font-medium text-[#65713a] hover:underline"
onClick={() => setLogsOpen((open) => !open)}
type="button"
>
<ScrollText className="size-3.5" />
{logsOpen
? "Show recent activity"
: `Show full activity log (${latestRun.attemptEvents.length})`}
<ChevronRight
className={`size-3.5 transition-transform ${logsOpen ? "rotate-90" : ""}`}
/>
</button>
) : null}
</div>
) : null}
</div>
) : (
<p className="mt-1 text-[#747168]">No implementation Run yet.</p>
@@ -303,9 +389,7 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
<Button
size="sm"
disabled={!slice.projectGitConnection}
onClick={() =>
void slice.startExecution(work._id, design?.slices?.[0]?.id)
}
onClick={() => void slice.startExecution(work._id)}
>
<Play className="size-3.5" /> Run
</Button>
@@ -315,11 +399,7 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
size="sm"
variant="outline"
onClick={() =>
void slice.startSimulation(
work._id,
"success",
design?.slices?.[0]?.id
)
void slice.startSimulation(work._id, "success")
}
>
<Play className="size-3.5" /> Simulate
@@ -338,7 +418,8 @@ const WorkCard = ({ onSourceSelect, work, slice }: WorkCardProps) => {
<X className="size-3.5" /> Cancel
</Button>
) : null}
{latestRun?.status === "terminal" &&
{latestRun?.executionKind !== "real" &&
latestRun?.status === "terminal" &&
latestRun.terminalClassification === "RetryableFailure" ? (
<Button
size="sm"
@@ -428,6 +509,7 @@ const ConnectProject = ({ slice }: { slice: SliceOneState }) => (
</main>
);
// oxlint-disable-next-line complexity -- this page coordinates the existing mobile shell without owning domain logic.
export const SliceOnePage = () => {
const slice = useSliceOne();
const viewportStyle = useVisualViewportStyle();
@@ -553,6 +635,22 @@ export const SliceOnePage = () => {
? `${slice.projectGitConnection.provider} · ${slice.projectGitConnection.serverUrl}`
: "No Git credentials attached"}
</p>
{slice.operationError ? (
<p className="mt-2 flex items-start gap-1.5 border border-red-300 bg-red-50 px-2 py-1.5 text-xs text-red-800">
<AlertTriangle className="mt-0.5 size-3.5 shrink-0" />
<span className="min-w-0 flex-1 leading-5">
{slice.operationError.message}
</span>
<button
aria-label="Dismiss error"
className="shrink-0"
onClick={() => slice.clearOperationError()}
type="button"
>
<X className="size-3.5" />
</button>
</p>
) : null}
<div className="mt-4 flex gap-2">
<Button
size="sm"

View File

@@ -40,7 +40,9 @@ interface WorkRecord {
_id: string;
kind: string;
message: string;
metadataJson: string;
occurredAt: number;
sequence: number;
}[];
readonly baseRevision?: string;
readonly candidateRevision?: string;
@@ -179,6 +181,20 @@ export const useSliceOne = () => {
const [repository, setRepository] = useState("");
const [pending, setPending] = useState(false);
const [error, setError] = useState<Error>();
const [operationError, setOperationError] = useState<Error>();
const captureOperationError =
<TArgs extends unknown[], TResult>(
operation: (...args: TArgs) => Promise<TResult>
) =>
async (...args: TArgs): Promise<TResult> => {
setOperationError(undefined);
try {
return await operation(...args);
} catch (caughtError) {
setOperationError(toError(caughtError));
throw caughtError;
}
};
const selectedProjectStillExists = projects?.some(
(project) => project.id === (selectedProjectId as unknown as string)
);
@@ -262,15 +278,18 @@ export const useSliceOne = () => {
| "permanent-failure"
| "cancelled",
sliceId?: string
) => startSimulationMutation({ scenario, sliceId, workId });
) =>
captureOperationError(() =>
startSimulationMutation({ scenario, sliceId, workId })
)();
const cancelSimulation = (runId: Id<"workRuns">) =>
cancelSimulationMutation({ runId });
captureOperationError(() => cancelSimulationMutation({ runId }))();
const retrySimulation = (runId: Id<"workRuns">) =>
retrySimulationMutation({ runId });
captureOperationError(() => retrySimulationMutation({ runId }))();
const startExecution = (workId: Id<"works">, sliceId?: string) =>
startExecutionMutation({ sliceId, workId });
captureOperationError(() => startExecutionMutation({ sliceId, workId }))();
const cancelExecution = (runId: Id<"workRuns">) =>
cancelExecutionMutation({ runId });
captureOperationError(() => cancelExecutionMutation({ runId }))();
const attachConnection = async (connectionId: Id<"gitConnections">) => {
if (!activeProjectId) {
throw new Error("Select a project first");
@@ -280,18 +299,16 @@ export const useSliceOne = () => {
projectId: activeProjectId,
});
};
const connectGitea = async (input: {
serverUrl: string;
token: string;
username?: string;
}) => {
const result = await connectGiteaAction(input);
await attachConnection(result.connectionId);
};
const connectLinkedGithub = async () => {
const connectGitea = captureOperationError(
async (input: { serverUrl: string; token: string; username?: string }) => {
const result = await connectGiteaAction(input);
await attachConnection(result.connectionId);
}
);
const connectLinkedGithub = captureOperationError(async () => {
const result = await connectGithubAction({});
await attachConnection(result.connectionId);
};
});
return {
agent,
@@ -300,11 +317,13 @@ export const useSliceOne = () => {
authorizeGithub,
cancelExecution,
cancelSimulation,
clearOperationError: () => setOperationError(undefined),
connectGitea,
connectLinkedGithub,
connectRepository,
error,
gitConnections,
operationError,
pending,
projectGitConnection,
projects,