feat(git-sync): client 'Git sync' provenance badge + git in runtime image (Phase D)
- page-history history-item: a lastUpdatedSource==='git-sync' version renders a neutral gray 'Git sync' badge (git-merge icon), NOT the agent badge/deep-link (it is not an agent edit). +2 i18n keys. - Dockerfile: install git in the installer (runtime) stage — VaultGit shells out to git, so assertGitAvailable() needs the binary at runtime. Client tsc clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,8 +17,9 @@ RUN pnpm build
|
||||
|
||||
FROM base AS installer
|
||||
|
||||
# git: required by the git-sync VaultGit (shells out to git)
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl bash \
|
||||
&& apt-get install -y --no-install-recommends curl bash git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -1171,6 +1171,8 @@
|
||||
"Ran tool {{name}}": "Ran tool {{name}}",
|
||||
"AI-agent": "AI-agent",
|
||||
"Edited by AI agent on behalf of {{name}}": "Edited by AI agent on behalf of {{name}}",
|
||||
"Git sync": "Git sync",
|
||||
"Synced from Git on behalf of {{name}}": "Synced from Git on behalf of {{name}}",
|
||||
"Endpoints": "Endpoints",
|
||||
"where we fetch models": "where we fetch models",
|
||||
"All endpoints are OpenAI-compatible. Point the Base URL at OpenAI, OpenRouter, a local Ollama, or any self-hosted server.": "All endpoints are OpenAI-compatible. Point the Base URL at OpenAI, OpenRouter, a local Ollama, or any self-hosted server.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Text, Group, UnstyledButton, Avatar, Tooltip, Badge } from "@mantine/core";
|
||||
import { IconSparkles } from "@tabler/icons-react";
|
||||
import { IconSparkles, IconGitMerge } from "@tabler/icons-react";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import { formattedDate } from "@/lib/time";
|
||||
import classes from "./css/history.module.css";
|
||||
@@ -107,6 +107,34 @@ function AiAgentBadge({
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Badge marking a version written by the git-sync VaultGit pull (provenance §8.1).
|
||||
* Like {@link AiAgentBadge} it is ADDITIVE — shown next to the human author —
|
||||
* but a git-sync edit is NOT an agent edit, so it is rendered as a small, neutral
|
||||
* badge with NO deep-link (there is no AI chat behind it).
|
||||
*/
|
||||
function GitSyncBadge({ authorName }: { authorName?: string }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const tooltip = t("Synced from Git on behalf of {{name}}", {
|
||||
name: authorName ?? "",
|
||||
});
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltip} withArrow>
|
||||
<Badge
|
||||
size="sm"
|
||||
variant="light"
|
||||
color="gray"
|
||||
radius="sm"
|
||||
leftSection={<IconGitMerge size={12} stroke={2} />}
|
||||
>
|
||||
{t("Git sync")}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const HistoryItem = memo(function HistoryItem({
|
||||
historyItem,
|
||||
index,
|
||||
@@ -126,6 +154,7 @@ const HistoryItem = memo(function HistoryItem({
|
||||
const contributors = historyItem.contributors;
|
||||
const hasContributors = contributors && contributors.length > 0;
|
||||
const isAgentEdit = historyItem.lastUpdatedSource === "agent";
|
||||
const isGitSyncEdit = historyItem.lastUpdatedSource === "git-sync";
|
||||
|
||||
return (
|
||||
<UnstyledButton
|
||||
@@ -190,6 +219,10 @@ const HistoryItem = memo(function HistoryItem({
|
||||
aiChatId={historyItem.lastUpdatedAiChatId}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isGitSyncEdit && (
|
||||
<GitSyncBadge authorName={historyItem.lastUpdatedBy?.name} />
|
||||
)}
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user