Skip to content

Commit 08104c9

Browse files
redxzetaAgent
andauthored
docs(web): add parody license changer warning copy (#39)
Co-authored-by: Agent <agent@debian.local>
1 parent 236fb54 commit 08104c9

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

apps/web/src/components/chat/ComposerPendingApprovalPanel.browser.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,34 @@ describe("ComposerPendingApprovalPanel", () => {
9393
}
9494
});
9595

96+
it("renders license-changer parody copy for permission requests", async () => {
97+
const mounted = await mountApprovalPanel({
98+
approval: makeApproval({
99+
requestKind: "permissions",
100+
detail: "Request to adjust LICENSE terms",
101+
permissionProfile: { license: { file: "LICENSE", readable: true }, copyrightOwner: "forkara-team" },
102+
}),
103+
});
104+
105+
try {
106+
await expect.element(
107+
page.getByText(
108+
"License Changer check: confirm legal ownership before proceeding",
109+
),
110+
).toBeInTheDocument();
111+
await expect.element(page.getByRole("link", { name: /Read current LICENSE/u })).toBeInTheDocument();
112+
await expect
113+
.element(
114+
page.getByText(
115+
"Changing license terms is not a cosmetic toggle. Verify ownership and repository policy before allowing",
116+
),
117+
)
118+
.toBeInTheDocument();
119+
} finally {
120+
await mounted.cleanup();
121+
}
122+
});
123+
96124
it("hides session approval when the provider cannot persist it", async () => {
97125
const mounted = await mountApprovalPanel({
98126
approval: makeApproval({ sessionApprovalAvailable: false }),

apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,32 @@ const KIND_PROMPT: Record<PendingApproval["requestKind"], string> = {
7676
permissions: "Grant these permissions?",
7777
};
7878

79+
const LICENSE_REFERENCE_URL = "https://github.com/redxzeta/forkara/blob/built-from-scratch/LICENSE";
80+
81+
const LICENSE_RELATED_KEYWORDS = ["license", "copyright", "copyright owner", "license_file", "license file"];
82+
83+
function hasLicenseSignals(input: { detail?: string; permissionProfile?: Record<string, unknown> }): boolean {
84+
const detail = input.detail?.toLowerCase() ?? "";
85+
const profile = input.permissionProfile;
86+
if (!detail && !profile) {
87+
return false;
88+
}
89+
if (detail && LICENSE_RELATED_KEYWORDS.some((keyword) => detail.includes(keyword))) {
90+
return true;
91+
}
92+
const profileText = profile ? JSON.stringify(profile).toLowerCase() : "";
93+
return LICENSE_RELATED_KEYWORDS.some((keyword) => profileText.includes(keyword));
94+
}
95+
7996
export const ComposerPendingApprovalPanel = function ComposerPendingApprovalPanel({
8097
approval,
8198
pendingCount,
8299
isResponding,
83100
onRespond,
84101
}: ComposerPendingApprovalPanelProps) {
85102
const parsed = parseApprovalDetail(approval.detail);
103+
const licenseProfileHint =
104+
approval.requestKind === "permissions" && hasLicenseSignals({ detail: approval.detail, permissionProfile: approval.permissionProfile });
86105
const requestId = approval.requestId;
87106
const actions =
88107
approval.sessionApprovalAvailable === false
@@ -116,7 +135,9 @@ export const ComposerPendingApprovalPanel = function ComposerPendingApprovalPane
116135
>
117136
<div className="flex items-start justify-between gap-3">
118137
<p className="min-w-0 text-[13px] font-medium leading-snug text-foreground/90">
119-
{KIND_PROMPT[approval.requestKind]}
138+
{licenseProfileHint
139+
? "License Changer check: confirm legal ownership before proceeding"
140+
: KIND_PROMPT[approval.requestKind]}
120141
{parsed.tool ? (
121142
<span className="ml-1.5 text-[11px] font-normal text-muted-foreground/50">
122143
{parsed.tool}
@@ -132,6 +153,7 @@ export const ComposerPendingApprovalPanel = function ComposerPendingApprovalPane
132153
<ApprovalDetail
133154
parsed={parsed}
134155
{...(approval.permissionProfile ? { permissionProfile: approval.permissionProfile } : {})}
156+
showLicenseChangerCopy={licenseProfileHint}
135157
/>
136158
<div className="mt-2.5 space-y-0.5">
137159
{actions.map((action, index) => (
@@ -160,11 +182,38 @@ export const ComposerPendingApprovalPanel = function ComposerPendingApprovalPane
160182
function ApprovalDetail({
161183
parsed,
162184
permissionProfile,
185+
showLicenseChangerCopy,
163186
}: {
164187
parsed: ParsedApproval;
165188
permissionProfile?: Record<string, unknown>;
189+
showLicenseChangerCopy?: boolean;
166190
}) {
167191
if (permissionProfile) {
192+
if (showLicenseChangerCopy) {
193+
return (
194+
<div className="mt-2">
195+
<p className="mb-1.5 text-[11.5px] leading-snug text-muted-foreground/70">
196+
Changing license terms is not a cosmetic toggle. Verify ownership and repository policy before allowing
197+
this, and keep the existing LICENSE as the source of truth.
198+
</p>
199+
<a
200+
href={LICENSE_REFERENCE_URL}
201+
target="_blank"
202+
rel="noreferrer"
203+
className="mb-1.5 inline-flex text-xs text-[var(--color-text-primary)] underline"
204+
>
205+
Read current LICENSE
206+
</a>
207+
<pre
208+
className="max-h-36 overflow-auto whitespace-pre-wrap break-words rounded-md bg-[var(--color-background-elevated-secondary)] px-2.5 py-2 font-mono text-[11px] leading-relaxed text-foreground/85"
209+
title="Requested permission profile"
210+
>
211+
<code>{JSON.stringify(permissionProfile, null, 2)}</code>
212+
</pre>
213+
</div>
214+
);
215+
}
216+
168217
return (
169218
<div className="mt-2">
170219
{parsed.fallback ? (

0 commit comments

Comments
 (0)