Remove api trigger kind and mark webhook as coming soon

Drop "api" from the trigger kind dropdown and disable the "webhook"
option with a "COMING SOON" label until it's ready.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-20 06:54:03 -05:00
commit 2a33acce3a
49 changed files with 1793 additions and 418 deletions

View file

@ -4,6 +4,7 @@ import {
currentUserAssigneeOption,
formatAssigneeUserLabel,
parseAssigneeValue,
suggestedCommentAssigneeValue,
} from "./assignees";
describe("assignee selection helpers", () => {
@ -50,4 +51,42 @@ describe("assignee selection helpers", () => {
expect(formatAssigneeUserLabel("local-board", "someone-else")).toBe("Board");
expect(formatAssigneeUserLabel("user-abcdef", "someone-else")).toBe("user-");
});
it("suggests the last non-me commenter without changing the actual assignee encoding", () => {
expect(
suggestedCommentAssigneeValue(
{ assigneeUserId: "board-user" },
[
{ authorUserId: "board-user" },
{ authorAgentId: "agent-123" },
],
"board-user",
),
).toBe("agent:agent-123");
});
it("falls back to the actual assignee when there is no better commenter hint", () => {
expect(
suggestedCommentAssigneeValue(
{ assigneeUserId: "board-user" },
[{ authorUserId: "board-user" }],
"board-user",
),
).toBe("user:board-user");
});
it("skips the current agent when choosing a suggested commenter assignee", () => {
expect(
suggestedCommentAssigneeValue(
{ assigneeUserId: "board-user" },
[
{ authorUserId: "board-user" },
{ authorAgentId: "agent-self" },
{ authorAgentId: "agent-123" },
],
null,
"agent-self",
),
).toBe("agent:agent-123");
});
});

View file

@ -9,12 +9,43 @@ export interface AssigneeOption {
searchText?: string;
}
interface CommentAssigneeSuggestionInput {
assigneeAgentId?: string | null;
assigneeUserId?: string | null;
}
interface CommentAssigneeSuggestionComment {
authorAgentId?: string | null;
authorUserId?: string | null;
}
export function assigneeValueFromSelection(selection: Partial<AssigneeSelection>): string {
if (selection.assigneeAgentId) return `agent:${selection.assigneeAgentId}`;
if (selection.assigneeUserId) return `user:${selection.assigneeUserId}`;
return "";
}
export function suggestedCommentAssigneeValue(
issue: CommentAssigneeSuggestionInput,
comments: CommentAssigneeSuggestionComment[] | null | undefined,
currentUserId: string | null | undefined,
currentAgentId?: string | null | undefined,
): string {
if (comments && comments.length > 0 && (currentUserId || currentAgentId)) {
for (let i = comments.length - 1; i >= 0; i--) {
const comment = comments[i];
if (comment.authorAgentId && comment.authorAgentId !== currentAgentId) {
return assigneeValueFromSelection({ assigneeAgentId: comment.authorAgentId });
}
if (comment.authorUserId && comment.authorUserId !== currentUserId) {
return assigneeValueFromSelection({ assigneeUserId: comment.authorUserId });
}
}
}
return assigneeValueFromSelection(issue);
}
export function parseAssigneeValue(value: string): AssigneeSelection {
if (!value) {
return { assigneeAgentId: null, assigneeUserId: null };

View file

@ -111,6 +111,10 @@ function makeRun(id: string, status: HeartbeatRun["status"], createdAt: string,
logCompressed: false,
errorCode: null,
externalRunId: null,
processPid: null,
processStartedAt: null,
retryOfRunId: null,
processLossRetryCount: 0,
stdoutExcerpt: null,
stderrExcerpt: null,
contextSnapshot: null,
@ -289,7 +293,11 @@ describe("inbox helpers", () => {
getInboxWorkItems({
issues: [olderIssue, newerIssue],
approvals: [approval],
}).map((item) => item.kind === "issue" ? `issue:${item.issue.id}` : `approval:${item.approval.id}`),
}).map((item) => {
if (item.kind === "issue") return `issue:${item.issue.id}`;
if (item.kind === "approval") return `approval:${item.approval.id}`;
return `run:${item.run.id}`;
}),
).toEqual([
"issue:1",
"approval:approval-between",

View file

@ -23,6 +23,11 @@ export type InboxWorkItem =
kind: "approval";
timestamp: number;
approval: Approval;
}
| {
kind: "failed_run";
timestamp: number;
run: HeartbeatRun;
};
export interface InboxBadgeData {
@ -146,9 +151,11 @@ export function approvalActivityTimestamp(approval: Approval): number {
export function getInboxWorkItems({
issues,
approvals,
failedRuns = [],
}: {
issues: Issue[];
approvals: Approval[];
failedRuns?: HeartbeatRun[];
}): InboxWorkItem[] {
return [
...issues.map((issue) => ({
@ -161,6 +168,11 @@ export function getInboxWorkItems({
timestamp: approvalActivityTimestamp(approval),
approval,
})),
...failedRuns.map((run) => ({
kind: "failed_run" as const,
timestamp: normalizeTimestamp(run.createdAt),
run,
})),
].sort((a, b) => {
const timestampDiff = b.timestamp - a.timestamp;
if (timestampDiff !== 0) return timestampDiff;