@agent-native/core 0.178.0 → 0.178.1-nightly-20260909221943
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/clips/.agents/skills/dictate/SKILL.md +1 -0
- package/corpus/templates/clips/DESIGN.md +10 -7
- package/corpus/templates/clips/actions/list-organization-state.ts +56 -3
- package/corpus/templates/clips/actions/list-recordings.ts +2 -0
- package/corpus/templates/clips/actions/search-dictations.ts +81 -0
- package/corpus/templates/clips/app/components/agent-destination-logos.tsx +50 -0
- package/corpus/templates/clips/app/components/bug-report/bug-report-dialog.tsx +60 -0
- package/corpus/templates/clips/app/components/bug-report/bug-report-form.tsx +206 -0
- package/corpus/templates/clips/app/components/clips-command-menu.tsx +653 -0
- package/corpus/templates/clips/app/components/library/empty-state.tsx +52 -17
- package/corpus/templates/clips/app/components/library/folder-card.tsx +34 -0
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +109 -64
- package/corpus/templates/clips/app/components/library/library-action-hrefs.ts +27 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +295 -103
- package/corpus/templates/clips/app/components/library/library-layout.tsx +381 -203
- package/corpus/templates/clips/app/components/library/library-primary-actions.tsx +48 -0
- package/corpus/templates/clips/app/components/library/page-header.tsx +48 -8
- package/corpus/templates/clips/app/components/library/recording-card.tsx +58 -55
- package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -19
- package/corpus/templates/clips/app/components/library/sidebar-feedback-button.tsx +48 -0
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +2 -2
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +344 -260
- package/corpus/templates/clips/app/components/player/viewer-controls.tsx +2 -2
- package/corpus/templates/clips/app/components/recorder/camera-visualizer.tsx +0 -33
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +5 -1
- package/corpus/templates/clips/app/components/ui/tabs.tsx +1 -1
- package/corpus/templates/clips/app/hooks/use-command-search.ts +53 -0
- package/corpus/templates/clips/app/hooks/use-navigation-state.ts +32 -13
- package/corpus/templates/clips/app/hooks/use-upload-video-picker.tsx +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +7 -0
- package/corpus/templates/clips/app/lib/agent-share.ts +16 -0
- package/corpus/templates/clips/app/lib/command-events.ts +7 -0
- package/corpus/templates/clips/app/root.tsx +6 -82
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +25 -5
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +2 -30
- package/corpus/templates/clips/app/routes/_app.library.folder.$folderId.tsx +13 -2
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +4 -17
- package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +111 -114
- package/corpus/templates/clips/app/routes/_app.spaces.$spaceId.folder.$folderId.tsx +25 -2
- package/corpus/templates/clips/app/routes/_app.spaces.$spaceId.tsx +14 -61
- package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +34 -24
- package/corpus/templates/clips/app/routes/_app.trash.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.tsx +2 -4
- package/corpus/templates/clips/app/routes/bug-report.tsx +11 -189
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +148 -123
- package/corpus/templates/clips/changelog/2026-09-08-comments-and-desktop-window-controls.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-08-global-command-search.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-09-agent-sidebar-fits-below-toolbar.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-09-desktop-switches-stay-clear-in-dark-mode.md +6 -0
- package/corpus/templates/clips/changelog/2026-09-09-recording-agent-uses-global-panel.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +2 -2
- package/corpus/templates/clips/desktop/src/components/MediaDeviceRow.tsx +5 -3
- package/corpus/templates/clips/desktop/src/components/SourceRow.tsx +1 -0
- package/corpus/templates/clips/desktop/src/components/Switch.tsx +2 -5
- package/corpus/templates/clips/desktop/src/components/ui/switch.tsx +21 -3
- package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +72 -52
- package/corpus/templates/clips/desktop/src/styles.css +18 -19
- package/corpus/templates/clips/desktop/src/tailwind.css +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +18 -10
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +3 -5
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +14 -3
- package/corpus/templates/design/AGENTS.md +2 -1
- package/corpus/templates/design/actions/export-png.ts +119 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +47 -13
- package/corpus/templates/design/server/plugins/agent-chat.ts +3 -1
- package/corpus/templates/factory/actions/dispatch-factory-item.ts +51 -22
- package/corpus/templates/factory/actions/list-triage-items.ts +21 -11
- package/corpus/templates/factory/actions/poll-slack-channel.ts +7 -2
- package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-keeps-claimed-markers-after-poll.md +6 -0
- package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-preserves-started-claimed-threads.md +6 -0
- package/corpus/templates/factory/changelog/2026-09-09-slack-feedback-skips-claimed-parents.md +6 -0
- package/corpus/templates/factory/server/lib/factory-automation-config.ts +1 -1
- package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +47 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +5 -4
- package/corpus/templates/factory/server/triage/review-skill-alignment.ts +12 -7
- package/corpus/templates/factory/server/triage/slack-review-window.ts +39 -0
- package/dist/client/CommandMenu.js +13 -1
- package/dist/client/analytics.js +10 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +5 -5
- package/dist/vite/client.js +25 -7
- package/dist/vite/sentry-source-maps.d.ts +12 -0
- package/dist/vite/sentry-source-maps.js +105 -0
- package/package.json +2 -1
|
@@ -48,6 +48,7 @@ import { and, eq } from "drizzle-orm";
|
|
|
48
48
|
import { z } from "zod";
|
|
49
49
|
|
|
50
50
|
import { getDb, schema } from "../server/db/index.js";
|
|
51
|
+
import { isAllowedFigmaSvgRenderRequest } from "../server/lib/design-to-figma-svg.js";
|
|
51
52
|
import "../server/db/index.js"; // ensure registerShareableResource runs
|
|
52
53
|
|
|
53
54
|
// ---------------------------------------------------------------------------
|
|
@@ -95,12 +96,22 @@ export interface ScreenshotDiagnostics {
|
|
|
95
96
|
zeroSizeOrOffscreen: DiagnosticsZeroSizeEntry[];
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
export type ScreenshotUploadError =
|
|
100
|
+
| {
|
|
101
|
+
code: "file_storage_not_configured";
|
|
102
|
+
message: string;
|
|
103
|
+
}
|
|
104
|
+
| {
|
|
105
|
+
code: "file_upload_failed";
|
|
106
|
+
message: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
98
109
|
export interface ScreenshotResult {
|
|
99
110
|
viewport: ScreenshotViewport;
|
|
100
111
|
url: string;
|
|
101
112
|
/** True when persisted via a durable upload provider. */
|
|
102
113
|
persisted: boolean;
|
|
103
|
-
uploadError?:
|
|
114
|
+
uploadError?: ScreenshotUploadError;
|
|
104
115
|
bytes: number;
|
|
105
116
|
diagnostics: ScreenshotDiagnostics;
|
|
106
117
|
}
|
|
@@ -586,6 +597,17 @@ export default defineAction({
|
|
|
586
597
|
await context.addInitScript(
|
|
587
598
|
"globalThis.__name = globalThis.__name || function (value) { return value; };",
|
|
588
599
|
);
|
|
600
|
+
await context.route("**/*", async (route) => {
|
|
601
|
+
if (await isAllowedFigmaSvgRenderRequest(route.request().url())) {
|
|
602
|
+
await route.continue();
|
|
603
|
+
} else {
|
|
604
|
+
await route.abort("blockedbyclient");
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
// A static PNG never needs a live socket. Leaving a routed WebSocket
|
|
608
|
+
// unconnected blocks the separate WebSocket transport that
|
|
609
|
+
// BrowserContext.route("**/*") does not see.
|
|
610
|
+
await context.routeWebSocket("**/*", () => {});
|
|
589
611
|
const page = await context.newPage();
|
|
590
612
|
const consoleErrors: string[] = [];
|
|
591
613
|
const fontLoadFailures: string[] = [];
|
|
@@ -677,23 +699,35 @@ export default defineAction({
|
|
|
677
699
|
// for tall complex screens, reproduced on a 1440x3200 fixture.
|
|
678
700
|
const png = await page.screenshot({ type: "png", fullPage: true });
|
|
679
701
|
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
702
|
+
let uploaded: Awaited<ReturnType<typeof uploadFile>> | null = null;
|
|
703
|
+
let uploadError: ScreenshotUploadError | undefined;
|
|
704
|
+
try {
|
|
705
|
+
uploaded = await uploadFile({
|
|
706
|
+
data: png,
|
|
707
|
+
mimeType: "image/png",
|
|
708
|
+
filename: `design-${file.designId}-${file.filename}-${viewport.label}.png`,
|
|
709
|
+
ownerEmail,
|
|
710
|
+
});
|
|
711
|
+
} catch {
|
|
712
|
+
uploadError = {
|
|
713
|
+
code: "file_upload_failed",
|
|
714
|
+
message:
|
|
715
|
+
"Configured file storage rejected the screenshot upload.",
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
if (!uploaded && !uploadError) {
|
|
719
|
+
uploadError = {
|
|
720
|
+
code: "file_storage_not_configured",
|
|
721
|
+
message:
|
|
722
|
+
"Screenshot was rendered but not returned because file storage is not configured.",
|
|
723
|
+
};
|
|
724
|
+
}
|
|
686
725
|
|
|
687
726
|
screenshots.push({
|
|
688
727
|
viewport,
|
|
689
728
|
url: uploaded?.url ?? "",
|
|
690
729
|
persisted: !!uploaded,
|
|
691
|
-
...(
|
|
692
|
-
? {}
|
|
693
|
-
: {
|
|
694
|
-
uploadError:
|
|
695
|
-
"Screenshot was rendered but not returned because file storage is not configured.",
|
|
696
|
-
}),
|
|
730
|
+
...(uploadError ? { uploadError } : {}),
|
|
697
731
|
bytes: png.byteLength,
|
|
698
732
|
diagnostics: {
|
|
699
733
|
viewport,
|
|
@@ -39,6 +39,7 @@ const EXTERNAL_CONNECTOR_TOOL_NAMES = [
|
|
|
39
39
|
"create-file",
|
|
40
40
|
"update-file",
|
|
41
41
|
"rename-screen",
|
|
42
|
+
"export-png",
|
|
42
43
|
];
|
|
43
44
|
|
|
44
45
|
const INITIAL_TOOL_NAMES = [
|
|
@@ -77,6 +78,7 @@ const INITIAL_TOOL_NAMES = [
|
|
|
77
78
|
"create-file",
|
|
78
79
|
"update-file",
|
|
79
80
|
"rename-screen",
|
|
81
|
+
"export-png",
|
|
80
82
|
"navigate",
|
|
81
83
|
"provider-api-catalog",
|
|
82
84
|
"provider-api-docs",
|
|
@@ -235,7 +237,7 @@ export default createAgentChatPlugin({
|
|
|
235
237
|
mcp: {
|
|
236
238
|
connectorCatalog: EXTERNAL_CONNECTOR_TOOL_NAMES,
|
|
237
239
|
instructions:
|
|
238
|
-
"Resolve a named template or prior design first with list-design-templates / list-designs; copy with create-design-from-template, then adapt with edit-design — never regenerate a copied screen with generate-design. For new-design exploration use create-design then present-design-variants (2-5 variants) and surface the returned open link; do not navigate. Hand-off goes through export-html / export-zip / export-coding-handoff / export-design-as-figma-svg. Persist early: create or update the design and its files as soon as a coherent candidate exists. " +
|
|
240
|
+
"Resolve a named template or prior design first with list-design-templates / list-designs; copy with create-design-from-template, then adapt with edit-design — never regenerate a copied screen with generate-design. For new-design exploration use create-design then present-design-variants (2-5 variants) and surface the returned open link; do not navigate. Hand-off goes through export-png for one screen, or export-html / export-zip / export-coding-handoff / export-design-as-figma-svg for other formats. Persist early: create or update the design and its files as soon as a coherent candidate exists. " +
|
|
239
241
|
'Design system: get-design, get-design-snapshot, and view-screen return `designSystem` (a bounded summary with scope "summary" and a `next` line); call get-design-system { id } once before the first screen you author for the full context (create-design returns it in full), then reuse it. Apply designSystem.agentContext, plus index-design-tokens for an existing design, before authoring or restyling; never invent a generic palette. For a new design, pass the exact title as `designSystem` or a designSystemId; omit both to link the caller\'s default. Preserve existing screen composition as well as linked system tokens, fonts, assets, and custom instructions. Read back the saved file after every visual mutation.',
|
|
240
242
|
},
|
|
241
243
|
externalAgents: { writes: "allowlisted" },
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
createSlackReader,
|
|
48
48
|
isAgentNativeSlackUserName,
|
|
49
49
|
} from "../server/triage/slack-client.js";
|
|
50
|
+
import { dispatchSkipStatusWrite } from "../server/triage/slack-review-window.js";
|
|
50
51
|
|
|
51
52
|
/** Slack notifies only with `<@USERID>`. Plaintext @handles do not ping anyone. */
|
|
52
53
|
const REPLY_INSTRUCTION =
|
|
@@ -280,7 +281,7 @@ export async function recordAutomaticBuilderDecision(input: {
|
|
|
280
281
|
userEmail: string;
|
|
281
282
|
orgId: string;
|
|
282
283
|
factoryId: string;
|
|
283
|
-
outcome: "propose_fix" | "needs_manual";
|
|
284
|
+
outcome: "propose_fix" | "needs_manual" | "observe";
|
|
284
285
|
reason: string;
|
|
285
286
|
guardResults: Array<{ code: string; passed: boolean; reason: string }>;
|
|
286
287
|
}) {
|
|
@@ -323,13 +324,20 @@ export async function recordAutomaticBuilderDecision(input: {
|
|
|
323
324
|
|
|
324
325
|
export default defineAction({
|
|
325
326
|
description:
|
|
326
|
-
"Tag Builder for a Factory item, or record a skip when clearBug is false. Slack items stay in-thread: this action pings Builder with the configured Slack member id; do not post Slack messages or @handles yourself. Pass optional reaction (an emoji name such as robot_face) to mark the item source when that provider can; omit it to add no reaction. Grouped Slack repeats share one Builder thread. GitHub issues and Sentry errors tag @builderio-bot on a GitHub issue in the factory repository. Owner-managed Clips, Design, and Content items are always left for their owner.",
|
|
327
|
+
"Tag Builder for a Factory item, or record a skip when clearBug is false or alreadyClaimed is true. Slack items stay in-thread: this action pings Builder with the configured Slack member id; do not post Slack messages or @handles yourself. Pass optional reaction (an emoji name such as robot_face) to mark the item source when that provider can; omit it to add no reaction. Grouped Slack repeats share one Builder thread. GitHub issues and Sentry errors tag @builderio-bot on a GitHub issue in the factory repository. Owner-managed Clips, Design, and Content items are always left for their owner.",
|
|
327
328
|
schema: z.object({
|
|
328
329
|
itemId: z.string().min(1),
|
|
330
|
+
alreadyClaimed: z
|
|
331
|
+
.boolean()
|
|
332
|
+
.default(false)
|
|
333
|
+
.describe(
|
|
334
|
+
"True when the Slack parent already has eyes or robot_face. Records the skip without starting Builder work: received items become needs_manual so they leave needsReview; items that already started keep their status. clearBug may be omitted or false.",
|
|
335
|
+
),
|
|
329
336
|
clearBug: z
|
|
330
337
|
.boolean()
|
|
338
|
+
.default(false)
|
|
331
339
|
.describe(
|
|
332
|
-
"True when the item is a concrete, reproducible defect with enough evidence to investigate (including visual/UI defects such as a duplicate control, broken layout, or incorrect state). False for feature requests, vague questions, and
|
|
340
|
+
"True when the item is a concrete, reproducible defect with enough evidence to investigate (including visual/UI defects such as a duplicate control, broken layout, or incorrect state). False for feature requests, vague questions, incomplete threads, and claimed Slack parents. May be omitted when alreadyClaimed is true.",
|
|
333
341
|
),
|
|
334
342
|
reason: z.string().trim().min(1).max(4_000),
|
|
335
343
|
productUxImplications: z
|
|
@@ -359,6 +367,7 @@ export default defineAction({
|
|
|
359
367
|
run: async (
|
|
360
368
|
{
|
|
361
369
|
itemId,
|
|
370
|
+
alreadyClaimed,
|
|
362
371
|
clearBug,
|
|
363
372
|
reason,
|
|
364
373
|
productUxImplications,
|
|
@@ -494,13 +503,19 @@ export default defineAction({
|
|
|
494
503
|
]
|
|
495
504
|
: []),
|
|
496
505
|
];
|
|
497
|
-
const blocked =
|
|
506
|
+
const blocked =
|
|
507
|
+
alreadyClaimed || guardResults.some((guard) => !guard.passed);
|
|
508
|
+
const skipStatus = dispatchSkipStatusWrite(item.status);
|
|
498
509
|
const decisionId = await recordAutomaticBuilderDecision({
|
|
499
510
|
itemId,
|
|
500
511
|
userEmail,
|
|
501
512
|
orgId,
|
|
502
513
|
factoryId,
|
|
503
|
-
outcome: blocked
|
|
514
|
+
outcome: blocked
|
|
515
|
+
? skipStatus.statusPreserved
|
|
516
|
+
? "observe"
|
|
517
|
+
: "needs_manual"
|
|
518
|
+
: "propose_fix",
|
|
504
519
|
reason,
|
|
505
520
|
guardResults,
|
|
506
521
|
});
|
|
@@ -518,35 +533,49 @@ export default defineAction({
|
|
|
518
533
|
summary: reason,
|
|
519
534
|
details: {
|
|
520
535
|
decisionId,
|
|
536
|
+
alreadyClaimed,
|
|
521
537
|
clearBug,
|
|
522
538
|
productUxImplications,
|
|
523
539
|
ownerOwnedArea: ownerManagedArea,
|
|
540
|
+
statusPreserved: blocked ? skipStatus.statusPreserved : false,
|
|
524
541
|
guardResults,
|
|
525
542
|
},
|
|
526
543
|
},
|
|
527
544
|
);
|
|
528
545
|
if (blocked) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
546
|
+
const nextStatus = skipStatus.nextStatus;
|
|
547
|
+
if (nextStatus) {
|
|
548
|
+
await db.transaction(async (tx) => {
|
|
549
|
+
await tx
|
|
550
|
+
.update(triageItems)
|
|
551
|
+
.set({
|
|
552
|
+
status: nextStatus,
|
|
553
|
+
updatedAt: new Date().toISOString(),
|
|
554
|
+
})
|
|
555
|
+
.where(
|
|
556
|
+
and(
|
|
557
|
+
eq(triageItems.id, itemId),
|
|
558
|
+
eq(triageItems.orgId, orgId),
|
|
559
|
+
factoryStillPresent(
|
|
560
|
+
tx as unknown as typeof db,
|
|
561
|
+
orgId,
|
|
562
|
+
factoryId,
|
|
563
|
+
),
|
|
564
|
+
),
|
|
565
|
+
);
|
|
566
|
+
await requireExistingFactory(
|
|
567
|
+
tx as unknown as typeof db,
|
|
568
|
+
orgId,
|
|
569
|
+
factoryId,
|
|
539
570
|
);
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
orgId,
|
|
543
|
-
factoryId,
|
|
544
|
-
);
|
|
545
|
-
});
|
|
571
|
+
});
|
|
572
|
+
}
|
|
546
573
|
return {
|
|
547
574
|
ok: true,
|
|
548
575
|
started: false,
|
|
549
|
-
needsManual:
|
|
576
|
+
needsManual: skipStatus.needsManual,
|
|
577
|
+
alreadyClaimed,
|
|
578
|
+
statusPreserved: skipStatus.statusPreserved,
|
|
550
579
|
decisionId,
|
|
551
580
|
reason,
|
|
552
581
|
};
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
triageItemAuthorId,
|
|
34
34
|
} from "../server/triage/metadata.js";
|
|
35
35
|
import { babysitLeavesReviewWindow } from "../server/triage/pr-babysit.js";
|
|
36
|
+
import { slackFeedbackLeavesReviewWindow } from "../server/triage/slack-review-window.js";
|
|
36
37
|
import { readStoredUserLabels } from "../server/triage/slack-user-labels.js";
|
|
37
38
|
|
|
38
39
|
export default defineAction({
|
|
@@ -79,24 +80,20 @@ export default defineAction({
|
|
|
79
80
|
const fetchLimit =
|
|
80
81
|
context?.caller === "automation" &&
|
|
81
82
|
calling &&
|
|
82
|
-
calling.config.source === "github"
|
|
83
|
+
(calling.config.source === "github" || calling.config.source === "slack")
|
|
83
84
|
? Math.min(100, Math.max(effectiveLimit * 10, effectiveLimit))
|
|
84
85
|
: effectiveLimit;
|
|
85
86
|
const parsedCursor = cursor ? decodeInboxCursor(cursor) : null;
|
|
86
87
|
const updatedAfterBound = parseUpdatedAfter(updatedAfter);
|
|
87
88
|
const db = getDb();
|
|
88
|
-
const reviewStatuses =
|
|
89
|
-
|
|
90
|
-
? ["pr_observed"]
|
|
91
|
-
: source === "slack"
|
|
92
|
-
? ["received", "automation_started", "evidence_ready"]
|
|
93
|
-
: ["received"];
|
|
94
|
-
const filterGithubReviewPage =
|
|
89
|
+
const reviewStatuses = source === "github" ? ["pr_observed"] : ["received"];
|
|
90
|
+
const filterReviewPage =
|
|
95
91
|
(context?.caller === "automation" &&
|
|
96
92
|
calling &&
|
|
97
|
-
calling.config.source === "github"
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
(calling.config.source === "github" ||
|
|
94
|
+
calling.config.source === "slack")) ||
|
|
95
|
+
(needsReview && (source === "github" || source === "slack"));
|
|
96
|
+
const maxScanPages = filterReviewPage ? 10 : 1;
|
|
100
97
|
const eligible: Array<(typeof triageItems)["$inferSelect"]> = [];
|
|
101
98
|
let scanCursor = parsedCursor;
|
|
102
99
|
let lastExamined: (typeof triageItems)["$inferSelect"] | undefined;
|
|
@@ -165,6 +162,19 @@ export default defineAction({
|
|
|
165
162
|
) {
|
|
166
163
|
continue;
|
|
167
164
|
}
|
|
165
|
+
if (
|
|
166
|
+
needsReview &&
|
|
167
|
+
source === "slack" &&
|
|
168
|
+
slackFeedbackLeavesReviewWindow({
|
|
169
|
+
status: item.status,
|
|
170
|
+
slackReactionName: metadataString(
|
|
171
|
+
parseTriageMetadata(item.metadataJson),
|
|
172
|
+
"slackReactionName",
|
|
173
|
+
),
|
|
174
|
+
})
|
|
175
|
+
) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
168
178
|
eligible.push(item);
|
|
169
179
|
lastKept = item;
|
|
170
180
|
if (eligible.length === effectiveLimit) {
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
import { recordFactoryAudit } from "../server/triage/audit.js";
|
|
27
27
|
import type { IngestionEnvelope } from "../server/triage/contracts.js";
|
|
28
28
|
import { itemDedupeKey } from "../server/triage/ids.js";
|
|
29
|
+
import { mergeTriageMetadata } from "../server/triage/metadata.js";
|
|
29
30
|
import {
|
|
30
31
|
hasTriageSourceChanged,
|
|
31
32
|
statusAfterTriageSourceUpdate,
|
|
@@ -178,6 +179,10 @@ export default defineAction({
|
|
|
178
179
|
const lastSeenAt = sourceChanged
|
|
179
180
|
? sourceLastSeenAt
|
|
180
181
|
: (existing?.lastSeenAt ?? now);
|
|
182
|
+
const metadataJson = mergeTriageMetadata(
|
|
183
|
+
existing?.metadataJson ?? "{}",
|
|
184
|
+
envelope.metadata ?? {},
|
|
185
|
+
);
|
|
181
186
|
await tx
|
|
182
187
|
.insert(triageItems)
|
|
183
188
|
.values({
|
|
@@ -196,7 +201,7 @@ export default defineAction({
|
|
|
196
201
|
headSha: envelope.headSha ?? null,
|
|
197
202
|
coverage: envelope.coverage,
|
|
198
203
|
dedupeKey: id,
|
|
199
|
-
metadataJson
|
|
204
|
+
metadataJson,
|
|
200
205
|
lastSeenAt,
|
|
201
206
|
createdAt: now,
|
|
202
207
|
updatedAt,
|
|
@@ -213,7 +218,7 @@ export default defineAction({
|
|
|
213
218
|
channelId: envelope.channelId ?? null,
|
|
214
219
|
threadTs: envelope.threadTs ?? null,
|
|
215
220
|
coverage: envelope.coverage,
|
|
216
|
-
metadataJson
|
|
221
|
+
metadataJson,
|
|
217
222
|
status,
|
|
218
223
|
lastSeenAt,
|
|
219
224
|
updatedAt,
|
|
@@ -479,7 +479,7 @@ export function buildGuardrailsText(
|
|
|
479
479
|
}
|
|
480
480
|
if (config.source === "slack") {
|
|
481
481
|
lines.push(
|
|
482
|
-
"Never post Slack messages, reactions, or plaintext @handles.
|
|
482
|
+
"Never post Slack messages, reactions, or plaintext @handles. Pass reaction on dispatch-factory-item only when the prompt says to mark that item; omit it on skips. That action adds it on the source when possible.",
|
|
483
483
|
);
|
|
484
484
|
}
|
|
485
485
|
const extraText = extra?.trim();
|
|
@@ -17,6 +17,44 @@ non-bug report.`;
|
|
|
17
17
|
const OBSOLETE_SLACK_TAG_PARAGRAPH =
|
|
18
18
|
/The Builder reply must tag @builder\.io[\s\S]*?non-bug\s+report\.\s*/;
|
|
19
19
|
|
|
20
|
+
const RETIRED_UNCONDITIONAL_ROBOT_FACE = `For each item, call dispatch-factory-item with clearBug true or false,
|
|
21
|
+
productUxImplications false unless it is a pure product or design decision
|
|
22
|
+
with no single correct fix, a short reason, and reaction robot_face 🤖.`;
|
|
23
|
+
|
|
24
|
+
const RETIRED_CLAIMED_SKIP_VIA_CLEAR_BUG_FALSE = `Look at the parent message reactions from get-slack-feedback-context. If the
|
|
25
|
+
parent already has eyes 👀 or robot_face 🤖, it has already been looked at:
|
|
26
|
+
call dispatch-factory-item with clearBug false, omit reaction, and a short
|
|
27
|
+
reason that names the existing marker. Do not start Builder work on it.
|
|
28
|
+
|
|
29
|
+
For every other item, call dispatch-factory-item with clearBug true or false,
|
|
30
|
+
productUxImplications false unless it is a pure product or design decision
|
|
31
|
+
with no single correct fix, and a short reason. Pass reaction robot_face 🤖
|
|
32
|
+
only when clearBug is true and the parent has neither eyes nor robot_face.
|
|
33
|
+
Omit reaction on skips.`;
|
|
34
|
+
|
|
35
|
+
const RETIRED_CLAIMED_SKIP_OMITS_CLEAR_BUG = `Look at the parent message reactions from get-slack-feedback-context. If the
|
|
36
|
+
parent already has eyes 👀 or robot_face 🤖, it has already been looked at:
|
|
37
|
+
call dispatch-factory-item with alreadyClaimed true, omit reaction, and a short
|
|
38
|
+
reason that names the existing marker. Do not start Builder work on it.
|
|
39
|
+
|
|
40
|
+
For every other item, call dispatch-factory-item with clearBug true or false,
|
|
41
|
+
productUxImplications false unless it is a pure product or design decision
|
|
42
|
+
with no single correct fix, and a short reason. Pass reaction robot_face 🤖
|
|
43
|
+
only when clearBug is true and the parent has neither eyes nor robot_face.
|
|
44
|
+
Omit reaction on skips.`;
|
|
45
|
+
|
|
46
|
+
export const SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS = `Look at the parent message reactions from get-slack-feedback-context. If the
|
|
47
|
+
parent already has eyes 👀 or robot_face 🤖, it has already been looked at:
|
|
48
|
+
call dispatch-factory-item with alreadyClaimed true (clearBug may be omitted
|
|
49
|
+
or false), omit reaction, and a short reason that names the existing marker.
|
|
50
|
+
Do not start Builder work on it.
|
|
51
|
+
|
|
52
|
+
For every other item, call dispatch-factory-item with clearBug true or false,
|
|
53
|
+
productUxImplications false unless it is a pure product or design decision
|
|
54
|
+
with no single correct fix, and a short reason. Pass reaction robot_face 🤖
|
|
55
|
+
only when clearBug is true and the parent has neither eyes nor robot_face.
|
|
56
|
+
Omit reaction on skips.`;
|
|
57
|
+
|
|
20
58
|
function stripPastedGuard(content: string, pasted: string): string {
|
|
21
59
|
return content.split(pasted).join("");
|
|
22
60
|
}
|
|
@@ -35,5 +73,14 @@ export function repairSlackFeedbackPrompt(content: string): string {
|
|
|
35
73
|
next = stripPastedGuard(next, PASTED_HANDOFF_INSTRUCTION);
|
|
36
74
|
next = stripPastedGuard(next, PASTED_MENTION_GUARD);
|
|
37
75
|
next = stripPastedGuard(next, PASTED_SKIP_GUARD);
|
|
76
|
+
next = next
|
|
77
|
+
.split(RETIRED_UNCONDITIONAL_ROBOT_FACE)
|
|
78
|
+
.join(SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS);
|
|
79
|
+
next = next
|
|
80
|
+
.split(RETIRED_CLAIMED_SKIP_VIA_CLEAR_BUG_FALSE)
|
|
81
|
+
.join(SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS);
|
|
82
|
+
next = next
|
|
83
|
+
.split(RETIRED_CLAIMED_SKIP_OMITS_CLEAR_BUG)
|
|
84
|
+
.join(SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS);
|
|
38
85
|
return `${next.replace(/\n{3,}/g, "\n\n").trimEnd()}\n`;
|
|
39
86
|
}
|
|
@@ -99,8 +99,11 @@ manual. Clips, Design, and Content are fully owner-managed: never react, tag
|
|
|
99
99
|
Builder, auto-approve, or auto-merge those items. Slack clear bugs use the
|
|
100
100
|
thread-preserving dispatch-factory-item flow; GitHub issues and Sentry clear
|
|
101
101
|
bugs tag @builderio-bot on a GitHub issue. Slack repeat reports must be clustered
|
|
102
|
-
by underlying symptom, with one Builder thread for the cluster. After classifying an item, call dispatch-factory-item
|
|
103
|
-
|
|
102
|
+
by underlying symptom, with one Builder thread for the cluster. After classifying an item, call dispatch-factory-item so the skip or
|
|
103
|
+
dispatch is recorded. If a Slack parent already has eyes or robot_face, pass
|
|
104
|
+
alreadyClaimed true (clearBug may be omitted or false) so started work is not
|
|
105
|
+
rewritten to needs_manual. Otherwise pass clearBug true or false and a short reason.
|
|
106
|
+
Pass reaction only when the prompt says to mark that item; omit it on skips. Do not post
|
|
104
107
|
Slack messages, reactions, or @mentions yourself; dispatch-factory-item owns
|
|
105
108
|
the Builder ping. Use /address-feedback for the repository feedback workflow.
|
|
106
109
|
For pull requests, follow review-prs: read the complete diff and review
|
|
@@ -56,7 +56,10 @@ import {
|
|
|
56
56
|
BABYSIT_SCOPE_INSTRUCTION,
|
|
57
57
|
repairPrBabysitPrompt,
|
|
58
58
|
} from "../lib/pr-babysit-prompt.js";
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
repairSlackFeedbackPrompt,
|
|
61
|
+
SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS,
|
|
62
|
+
} from "../lib/slack-feedback-prompt.js";
|
|
60
63
|
import {
|
|
61
64
|
syncManagedReviewSkillAlignment,
|
|
62
65
|
type FactoryAutomationName,
|
|
@@ -225,9 +228,7 @@ enough evidence to investigate — including visual/UI defects such as a
|
|
|
225
228
|
duplicate control or broken layout. Feature requests, vague questions, and
|
|
226
229
|
incomplete threads are not.
|
|
227
230
|
|
|
228
|
-
|
|
229
|
-
productUxImplications false unless it is a pure product or design decision
|
|
230
|
-
with no single correct fix, a short reason, and reaction robot_face 🤖.
|
|
231
|
+
${SLACK_FEEDBACK_DISPATCH_INSTRUCTIONS}
|
|
231
232
|
Cluster only items listed in this run: one dispatch with relatedItemIds. Do
|
|
232
233
|
not dispatch needs_manual items or items that already started.
|
|
233
234
|
|
|
@@ -21,10 +21,14 @@ contract is evidence-first and reply-producing:
|
|
|
21
21
|
state.
|
|
22
22
|
- Check the existing Slack reaction marker and owner before any write.
|
|
23
23
|
Preserve an existing marker; if reactions cannot be read, do not guess or
|
|
24
|
-
add one.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
add one. If the parent already has eyes 👀 or robot_face 🤖, call
|
|
25
|
+
\`dispatch-factory-item\` with \`alreadyClaimed: true\` (\`clearBug\` may be
|
|
26
|
+
omitted or \`false\`), omit reaction, and do not start Builder work. Do not
|
|
27
|
+
react to or dispatch Design UX/interaction
|
|
28
|
+
work (Sid) or any Content work (Alice); record the owner instead.
|
|
29
|
+
- For an actionable repo-owned Slack item with no existing eyes 👀 or
|
|
30
|
+
robot_face 🤖 marker, pass \`reaction: robot_face\` 🤖 on
|
|
31
|
+
\`dispatch-factory-item\`. Do not pass reaction eyes. Every parent this run
|
|
28
32
|
marks must later receive a verified @agent-native Fixed, In progress, or
|
|
29
33
|
Clarification needed reply; a reaction, forward, generic acknowledgement,
|
|
30
34
|
or another person's reply is not a disposition. Group only genuinely
|
|
@@ -37,9 +41,10 @@ contract is evidence-first and reply-producing:
|
|
|
37
41
|
separate. Do not claim a fix, PR, reply, or deployment without confirmation
|
|
38
42
|
from the relevant action or runtime evidence.
|
|
39
43
|
|
|
40
|
-
After classifying every processed item, call \`dispatch-factory-item\`
|
|
41
|
-
\`
|
|
42
|
-
|
|
44
|
+
After classifying every processed item, call \`dispatch-factory-item\` so every
|
|
45
|
+
skip or dispatch is recorded: \`alreadyClaimed: true\` (\`clearBug\` may be
|
|
46
|
+
omitted or \`false\`) when the parent already has eyes or robot_face, otherwise
|
|
47
|
+
\`clearBug: true\` or \`false\` with a concise evidence-grounded reason.`;
|
|
43
48
|
|
|
44
49
|
const PR_ALIGNMENT = `## Current review-prs contract
|
|
45
50
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function isClaimedSlackReactionName(
|
|
2
|
+
name: string | null | undefined,
|
|
3
|
+
): boolean {
|
|
4
|
+
const normalized = name?.trim().toLowerCase();
|
|
5
|
+
return normalized === "eyes" || normalized === "robot_face";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function dispatchSkipPreservesItemStatus(status: string): boolean {
|
|
9
|
+
return status === "automation_started" || status === "evidence_ready";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function slackFeedbackLeavesReviewWindow(input: {
|
|
13
|
+
status: string;
|
|
14
|
+
slackReactionName?: string | null;
|
|
15
|
+
}): boolean {
|
|
16
|
+
return (
|
|
17
|
+
dispatchSkipPreservesItemStatus(input.status) ||
|
|
18
|
+
isClaimedSlackReactionName(input.slackReactionName)
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function dispatchSkipStatusWrite(status: string): {
|
|
23
|
+
nextStatus: "needs_manual" | null;
|
|
24
|
+
needsManual: boolean;
|
|
25
|
+
statusPreserved: boolean;
|
|
26
|
+
} {
|
|
27
|
+
if (dispatchSkipPreservesItemStatus(status)) {
|
|
28
|
+
return {
|
|
29
|
+
nextStatus: null,
|
|
30
|
+
needsManual: false,
|
|
31
|
+
statusPreserved: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
nextStatus: "needs_manual",
|
|
36
|
+
needsManual: true,
|
|
37
|
+
statusPreserved: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -224,6 +224,15 @@ export function CommandMenu({ open, onOpenChange, children, renderResults, place
|
|
|
224
224
|
if (!React.isValidElement(child))
|
|
225
225
|
return child;
|
|
226
226
|
const props = child.props;
|
|
227
|
+
if (child.type === React.Fragment) {
|
|
228
|
+
const fragmentChildren = filterChildren(props.children);
|
|
229
|
+
if (React.Children.count(fragmentChildren) === 0)
|
|
230
|
+
return null;
|
|
231
|
+
return React.cloneElement(child, {
|
|
232
|
+
...props,
|
|
233
|
+
children: fragmentChildren,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
227
236
|
// If it's a CommandGroup, filter its children
|
|
228
237
|
if (child.type === CommandGroup) {
|
|
229
238
|
const groupChildren = filterChildren(props.children);
|
|
@@ -324,6 +333,10 @@ export function useCommandMenuShortcut(onOpen, options = {}) {
|
|
|
324
333
|
useEffect(() => {
|
|
325
334
|
const handleKeyDown = (e) => {
|
|
326
335
|
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
|
|
336
|
+
// Claim the shortcut before checking the focused element so an outer
|
|
337
|
+
// host cannot open its own command menu while this one is focused.
|
|
338
|
+
e.preventDefault();
|
|
339
|
+
e.stopPropagation();
|
|
327
340
|
// Don't trigger if user is typing in a native form control.
|
|
328
341
|
const target = e.target instanceof HTMLElement ? e.target : null;
|
|
329
342
|
const isContentEditable = target?.isContentEditable;
|
|
@@ -333,7 +346,6 @@ export function useCommandMenuShortcut(onOpen, options = {}) {
|
|
|
333
346
|
(!options.allowContentEditable && isContentEditable)) {
|
|
334
347
|
return;
|
|
335
348
|
}
|
|
336
|
-
e.preventDefault();
|
|
337
349
|
onOpen();
|
|
338
350
|
}
|
|
339
351
|
};
|
package/dist/client/analytics.js
CHANGED
|
@@ -7,6 +7,7 @@ import { toPostHogExceptionProperties } from "../tracking/posthog-exception.js";
|
|
|
7
7
|
import { getAnalyticsClientPlatform } from "./analytics-platform.js";
|
|
8
8
|
import { getOrCreateAnalyticsAnonymousId, getOrCreateAnalyticsSessionId, } from "./analytics-session.js";
|
|
9
9
|
import { injectedAgentNativeConfig } from "./app-config.js";
|
|
10
|
+
import { clientBuildId } from "./build-compatibility.js";
|
|
10
11
|
export { clearAnalyticsSessionId, setAnalyticsSessionId, } from "./analytics-session.js";
|
|
11
12
|
import { fetchAgentEngineStatus, fetchAuthSessionStatus, } from "./client-status-requests.js";
|
|
12
13
|
import { installErrorCapture, } from "./error-capture.js";
|
|
@@ -721,6 +722,14 @@ function resolveClientDeploymentEnvironment() {
|
|
|
721
722
|
env.MODE ||
|
|
722
723
|
"production");
|
|
723
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Must match `resolveSentryClientRelease()` in `vite/sentry-source-maps.ts`
|
|
727
|
+
* exactly — that's the release name uploaded source maps are attached to, so
|
|
728
|
+
* a mismatch here means captured events never resolve against them.
|
|
729
|
+
*/
|
|
730
|
+
function resolveClientRelease() {
|
|
731
|
+
return `agent-native-client@${clientBuildId() || "development"}`;
|
|
732
|
+
}
|
|
724
733
|
function captureWithSentry(module, error, context) {
|
|
725
734
|
return module.withScope((scope) => {
|
|
726
735
|
if (context.tags) {
|
|
@@ -764,6 +773,7 @@ function ensureSentry(loadWithoutDsn = false) {
|
|
|
764
773
|
module.init({
|
|
765
774
|
dsn,
|
|
766
775
|
environment: resolveClientDeploymentEnvironment(),
|
|
776
|
+
release: resolveClientRelease(),
|
|
767
777
|
beforeSend(event) {
|
|
768
778
|
if (isSyntheticBrowserTraffic())
|
|
769
779
|
return null;
|
package/dist/collab/routes.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
|
|
|
26
26
|
* Body: { update: string (base64), requestSource?: string }
|
|
27
27
|
*/
|
|
28
28
|
export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
29
|
-
error: string;
|
|
30
29
|
ok?: undefined;
|
|
30
|
+
error: string;
|
|
31
31
|
} | {
|
|
32
32
|
error?: undefined;
|
|
33
33
|
ok: boolean;
|
|
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
|
|
|
17
17
|
id?: undefined;
|
|
18
18
|
provider?: undefined;
|
|
19
19
|
} | {
|
|
20
|
+
error?: undefined;
|
|
20
21
|
configured?: undefined;
|
|
21
22
|
connectPath?: undefined;
|
|
22
23
|
url: string;
|
|
23
24
|
id: string;
|
|
24
25
|
provider: string;
|
|
25
|
-
error?: undefined;
|
|
26
26
|
}>;
|
|
27
27
|
export default _default;
|