@builder.io/ai-utils 0.71.1 → 0.73.0
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/package.json +1 -1
- package/src/codegen/investigation-context.d.ts +22 -0
- package/src/codegen/investigation-context.js +147 -0
- package/src/codegen.d.ts +49 -3
- package/src/codegen.js +19 -0
- package/src/design-systems.d.ts +63 -10
- package/src/design-systems.js +51 -7
- package/src/events.d.ts +2 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +1 -0
- package/src/projects.d.ts +12 -0
- package/src/projects.js +7 -0
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ContentMessage, MessageParam } from "../messages.js";
|
|
2
|
+
export declare const MAX_TOOL_RESULT_CHARS = 8000;
|
|
3
|
+
export declare const MAX_SYSTEM_PROMPT_CHARS = 60000;
|
|
4
|
+
export declare const MAX_TRANSCRIPT_CHARS = 160000;
|
|
5
|
+
export interface InvestigationEvent {
|
|
6
|
+
id: string;
|
|
7
|
+
hasError?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface InvestigationCompletionJSON {
|
|
10
|
+
model?: string;
|
|
11
|
+
systemPrompt?: string;
|
|
12
|
+
messages?: MessageParam[];
|
|
13
|
+
}
|
|
14
|
+
export declare function serializeContentToBlocks(message: string | ContentMessage | undefined): string;
|
|
15
|
+
export declare function messagesToText(messages: MessageParam[]): string;
|
|
16
|
+
export declare function truncateToolResultsInTranscript(text: string): string;
|
|
17
|
+
export interface BuildInvestigationSystemPromptInput {
|
|
18
|
+
event: InvestigationEvent;
|
|
19
|
+
completionJson: InvestigationCompletionJSON | undefined;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildInvestigationSystemPrompt({ event, completionJson }: BuildInvestigationSystemPromptInput): string;
|
|
22
|
+
export declare const SUGGESTED_QUESTIONS: readonly string[];
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
export const MAX_TOOL_RESULT_CHARS = 8000;
|
|
2
|
+
export const MAX_SYSTEM_PROMPT_CHARS = 60000;
|
|
3
|
+
export const MAX_TRANSCRIPT_CHARS = 160000;
|
|
4
|
+
function truncate(value, max) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return "";
|
|
7
|
+
if (value.length <= max)
|
|
8
|
+
return value;
|
|
9
|
+
const removed = value.length - max;
|
|
10
|
+
return `${value.slice(0, max)}\n\n[truncated ${removed} chars]`;
|
|
11
|
+
}
|
|
12
|
+
// Block-serializer used by messagesToText and the dashboard LLM tab. Distinct
|
|
13
|
+
// from the simpler text-only `getContentText` exported from messages.ts.
|
|
14
|
+
export function serializeContentToBlocks(message) {
|
|
15
|
+
if (!message) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
if (typeof message === "string") {
|
|
19
|
+
return message;
|
|
20
|
+
}
|
|
21
|
+
if (!Array.isArray(message)) {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
return message
|
|
25
|
+
.map((item) => {
|
|
26
|
+
var _a;
|
|
27
|
+
if (item.type === "text") {
|
|
28
|
+
return `<__llm_block__ type="text">\n${item.text}\n</__llm_block__>`;
|
|
29
|
+
}
|
|
30
|
+
else if (item.type === "image") {
|
|
31
|
+
return `<__llm_block__ type="image">${item.source.type === "base64"
|
|
32
|
+
? ((_a = item.source.original_url) !== null && _a !== void 0 ? _a : "")
|
|
33
|
+
: item.source.url}</__llm_block__>`;
|
|
34
|
+
}
|
|
35
|
+
else if (item.type === "document") {
|
|
36
|
+
return `<__llm_block__ type="document" source_type="${item.source.type}" title="${item.title}">\n${item.source.type === "text" ? item.source.data : ""}\n</__llm_block__>`;
|
|
37
|
+
}
|
|
38
|
+
else if (item.type === "tool_use") {
|
|
39
|
+
return `<__llm_block__ type="tool_use" name="${item.name}" id="${item.id}">\n${JSON.stringify(item.input, null, 2)}\n</__llm_block__>`;
|
|
40
|
+
}
|
|
41
|
+
else if (item.type === "tool_result") {
|
|
42
|
+
return `<__llm_block__ type="tool_result" tool_use_id="${item.tool_use_id}" is_error="${item.is_error}">\n${serializeContentToBlocks(item.content)}\n</__llm_block__>`;
|
|
43
|
+
}
|
|
44
|
+
else if (item.type === "thinking") {
|
|
45
|
+
return `<__llm_block__ type="thinking" signature="${item.signature}">\n${item.thinking}\n</__llm_block__>`;
|
|
46
|
+
}
|
|
47
|
+
return "";
|
|
48
|
+
})
|
|
49
|
+
.join("\n\n");
|
|
50
|
+
}
|
|
51
|
+
export function messagesToText(messages) {
|
|
52
|
+
return messages
|
|
53
|
+
.map((message) => {
|
|
54
|
+
const content = serializeContentToBlocks(message.content);
|
|
55
|
+
return `<___llm_message___ role=${JSON.stringify(message.role)}>\n${content}\n</___llm_message___>`;
|
|
56
|
+
})
|
|
57
|
+
.join("\n\n");
|
|
58
|
+
}
|
|
59
|
+
export function truncateToolResultsInTranscript(text) {
|
|
60
|
+
// Truncate any oversized tool_result block content so a single huge tool
|
|
61
|
+
// result doesn't blow the context budget. tool_result bodies can contain
|
|
62
|
+
// nested __llm_block__ tags (recursive serialization), so we depth-track
|
|
63
|
+
// open/close to find the matching close tag rather than the first one.
|
|
64
|
+
const open = '<__llm_block__ type="tool_result"';
|
|
65
|
+
const anyOpen = "<__llm_block__";
|
|
66
|
+
const close = "</__llm_block__>";
|
|
67
|
+
let result = "";
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
while (true) {
|
|
70
|
+
const start = text.indexOf(open, cursor);
|
|
71
|
+
if (start === -1) {
|
|
72
|
+
result += text.slice(cursor);
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
result += text.slice(cursor, start);
|
|
76
|
+
const tagEnd = text.indexOf(">", start);
|
|
77
|
+
if (tagEnd === -1) {
|
|
78
|
+
result += text.slice(start);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
let depth = 1;
|
|
82
|
+
let scan = tagEnd + 1;
|
|
83
|
+
let blockEnd = -1;
|
|
84
|
+
while (scan < text.length) {
|
|
85
|
+
const nextOpen = text.indexOf(anyOpen, scan);
|
|
86
|
+
const nextClose = text.indexOf(close, scan);
|
|
87
|
+
if (nextClose === -1)
|
|
88
|
+
break;
|
|
89
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
90
|
+
depth++;
|
|
91
|
+
scan = nextOpen + anyOpen.length;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
depth--;
|
|
95
|
+
if (depth === 0) {
|
|
96
|
+
blockEnd = nextClose;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
scan = nextClose + close.length;
|
|
100
|
+
}
|
|
101
|
+
if (blockEnd === -1) {
|
|
102
|
+
result += text.slice(start);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const header = text.slice(start, tagEnd + 1);
|
|
106
|
+
const body = text.slice(tagEnd + 1, blockEnd);
|
|
107
|
+
const truncated = truncate(body, MAX_TOOL_RESULT_CHARS);
|
|
108
|
+
result += header + truncated + close;
|
|
109
|
+
cursor = blockEnd + close.length;
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
export function buildInvestigationSystemPrompt({ event, completionJson, }) {
|
|
114
|
+
var _a, _b;
|
|
115
|
+
const role = `You are a senior AI engineer helping debug a single codegen agent call.
|
|
116
|
+
You have exactly the same context the engineer sees on the LLM tab for this
|
|
117
|
+
event: the system prompt and the message transcript (with tool_use,
|
|
118
|
+
tool_result, and thinking blocks). Be concise, specific, and critical.
|
|
119
|
+
|
|
120
|
+
Ground every answer in the concrete data below: cite specific tool calls
|
|
121
|
+
(by name + id), specific messages (by role + index), or system prompt
|
|
122
|
+
sections. When you suggest improvements, propose concrete changes (e.g.
|
|
123
|
+
"remove tool X", "add this instruction", "split this prompt section"). If
|
|
124
|
+
the data does not support a claim, say so.`;
|
|
125
|
+
const header = `## Event\n\nid: ${event.id}\nmodel: ${(_a = completionJson === null || completionJson === void 0 ? void 0 : completionJson.model) !== null && _a !== void 0 ? _a : "(unknown)"}${event.hasError ? "\nstatus: ERROR" : ""}`;
|
|
126
|
+
// Use XML-style delimiters instead of triple-backtick fences: codegen
|
|
127
|
+
// transcripts often contain markdown code fences themselves which would
|
|
128
|
+
// otherwise close the outer block and corrupt the prompt.
|
|
129
|
+
const systemPromptBlock = `## System prompt (verbatim)\n\n<___event_system_prompt___>\n${truncate((_b = completionJson === null || completionJson === void 0 ? void 0 : completionJson.systemPrompt) !== null && _b !== void 0 ? _b : "(no system prompt)", MAX_SYSTEM_PROMPT_CHARS)}\n</___event_system_prompt___>`;
|
|
130
|
+
const transcriptText = (completionJson === null || completionJson === void 0 ? void 0 : completionJson.messages)
|
|
131
|
+
? truncateToolResultsInTranscript(messagesToText(completionJson.messages))
|
|
132
|
+
: "(no messages)";
|
|
133
|
+
const transcriptBlock = `## Message transcript (tool_use / tool_result / thinking)\n\n<___event_transcript___>\n${truncate(transcriptText, MAX_TRANSCRIPT_CHARS)}\n</___event_transcript___>`;
|
|
134
|
+
const guidance = `## How to answer
|
|
135
|
+
|
|
136
|
+
- Start with a 1-2 sentence direct answer.
|
|
137
|
+
- Back it up with specific evidence (tool names, message indices, prompt phrases).
|
|
138
|
+
- End with concrete improvement suggestions when applicable.`;
|
|
139
|
+
return [role, header, systemPromptBlock, transcriptBlock, guidance].join("\n\n");
|
|
140
|
+
}
|
|
141
|
+
export const SUGGESTED_QUESTIONS = [
|
|
142
|
+
"Why did this call fail or behave suboptimally?",
|
|
143
|
+
"Which tool calls were wasteful or redundant?",
|
|
144
|
+
"What context was missing that would have helped?",
|
|
145
|
+
"How could the system prompt be improved for this case?",
|
|
146
|
+
"Walk me through the agent's reasoning step by step.",
|
|
147
|
+
];
|
package/src/codegen.d.ts
CHANGED
|
@@ -1331,6 +1331,15 @@ export declare const IDEDiagnosticsToolInputSchema: z.ZodObject<{
|
|
|
1331
1331
|
file_path: z.ZodOptional<z.ZodString>;
|
|
1332
1332
|
}, z.core.$strip>;
|
|
1333
1333
|
export type IDEDiagnosticsToolInput = z.infer<typeof IDEDiagnosticsToolInputSchema>;
|
|
1334
|
+
export declare const GetCodegenEventToolInputSchema: z.ZodObject<{
|
|
1335
|
+
event_id: z.ZodString;
|
|
1336
|
+
}, z.core.$strip>;
|
|
1337
|
+
export type GetCodegenEventToolInput = z.infer<typeof GetCodegenEventToolInputSchema>;
|
|
1338
|
+
export declare const ListCodegenSessionEventsToolInputSchema: z.ZodObject<{
|
|
1339
|
+
session_or_event_id: z.ZodString;
|
|
1340
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
1341
|
+
}, z.core.$strip>;
|
|
1342
|
+
export type ListCodegenSessionEventsToolInput = z.infer<typeof ListCodegenSessionEventsToolInputSchema>;
|
|
1334
1343
|
export declare const PullPrototypeToolInputSchema: z.ZodObject<{
|
|
1335
1344
|
url: z.ZodString;
|
|
1336
1345
|
project_id: z.ZodOptional<z.ZodString>;
|
|
@@ -1911,6 +1920,13 @@ export declare const CodeGenToolMapSchema: z.ZodObject<{
|
|
|
1911
1920
|
draft: z.ZodOptional<z.ZodBoolean>;
|
|
1912
1921
|
}, z.core.$strip>;
|
|
1913
1922
|
GenerateDesignSystemAgentMd: z.ZodObject<{}, z.core.$strip>;
|
|
1923
|
+
GetCodegenEvent: z.ZodObject<{
|
|
1924
|
+
event_id: z.ZodString;
|
|
1925
|
+
}, z.core.$strip>;
|
|
1926
|
+
ListCodegenSessionEvents: z.ZodObject<{
|
|
1927
|
+
session_or_event_id: z.ZodString;
|
|
1928
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
1929
|
+
}, z.core.$strip>;
|
|
1914
1930
|
}, z.core.$strip>;
|
|
1915
1931
|
export type CodeGenToolMap = z.infer<typeof CodeGenToolMapSchema>;
|
|
1916
1932
|
export declare const CodeGenToolsSchema: z.ZodEnum<{
|
|
@@ -1935,12 +1951,14 @@ export declare const CodeGenToolsSchema: z.ZodEnum<{
|
|
|
1935
1951
|
FindMedia: "FindMedia";
|
|
1936
1952
|
GenerateDesignSystemAgentMd: "GenerateDesignSystemAgentMd";
|
|
1937
1953
|
GetAvailableRepos: "GetAvailableRepos";
|
|
1954
|
+
GetCodegenEvent: "GetCodegenEvent";
|
|
1938
1955
|
GetLastBrowserTest: "GetLastBrowserTest";
|
|
1939
1956
|
GetScreenshot: "GetScreenshot";
|
|
1940
1957
|
GetStyleInspiration: "GetStyleInspiration";
|
|
1941
1958
|
Glob: "Glob";
|
|
1942
1959
|
Grep: "Grep";
|
|
1943
1960
|
IDEDiagnostics: "IDEDiagnostics";
|
|
1961
|
+
ListCodegenSessionEvents: "ListCodegenSessionEvents";
|
|
1944
1962
|
Media: "Media";
|
|
1945
1963
|
MultiEdit: "MultiEdit";
|
|
1946
1964
|
NavigatePreview: "NavigatePreview";
|
|
@@ -2652,12 +2670,14 @@ export declare const CodeGenInputOptionsSchema: z.ZodObject<{
|
|
|
2652
2670
|
FindMedia: "FindMedia";
|
|
2653
2671
|
GenerateDesignSystemAgentMd: "GenerateDesignSystemAgentMd";
|
|
2654
2672
|
GetAvailableRepos: "GetAvailableRepos";
|
|
2673
|
+
GetCodegenEvent: "GetCodegenEvent";
|
|
2655
2674
|
GetLastBrowserTest: "GetLastBrowserTest";
|
|
2656
2675
|
GetScreenshot: "GetScreenshot";
|
|
2657
2676
|
GetStyleInspiration: "GetStyleInspiration";
|
|
2658
2677
|
Glob: "Glob";
|
|
2659
2678
|
Grep: "Grep";
|
|
2660
2679
|
IDEDiagnostics: "IDEDiagnostics";
|
|
2680
|
+
ListCodegenSessionEvents: "ListCodegenSessionEvents";
|
|
2661
2681
|
Media: "Media";
|
|
2662
2682
|
MultiEdit: "MultiEdit";
|
|
2663
2683
|
NavigatePreview: "NavigatePreview";
|
|
@@ -3540,12 +3560,10 @@ export interface UserInput {
|
|
|
3540
3560
|
/** @deprecated */
|
|
3541
3561
|
repair?: boolean;
|
|
3542
3562
|
}
|
|
3543
|
-
|
|
3544
|
-
state: "running" | "done" | "error";
|
|
3563
|
+
interface CodegenTurnBase {
|
|
3545
3564
|
unixTime: number;
|
|
3546
3565
|
completionId: string;
|
|
3547
3566
|
title: string;
|
|
3548
|
-
nextUrl: string | undefined;
|
|
3549
3567
|
user: UserSource;
|
|
3550
3568
|
creditsUsed: number;
|
|
3551
3569
|
actions: ActionItem[];
|
|
@@ -3557,7 +3575,34 @@ export interface CodegenTurn {
|
|
|
3557
3575
|
lastCommit: GitSnapshot | undefined;
|
|
3558
3576
|
cachedToolResults?: ContentMessageItemToolResult[];
|
|
3559
3577
|
autoContinue: boolean;
|
|
3578
|
+
/**
|
|
3579
|
+
* Only set on synthetic per-user-message checkpoints (the non-linear
|
|
3580
|
+
* `getSessionTurns` result). `completionId` always stays the checkpoint's
|
|
3581
|
+
* own identity — for these synthetic turns, the user message's id — which is
|
|
3582
|
+
* exactly what restoring "before this point" needs (the factual commit at
|
|
3583
|
+
* that moment). `finalCompletionId` separately carries the last agent turn in
|
|
3584
|
+
* the message's range that holds the post-message commit (`afterCommit`), for
|
|
3585
|
+
* callers that want to restore to the "after" state. Undefined for ordinary
|
|
3586
|
+
* (linear) turns.
|
|
3587
|
+
*/
|
|
3588
|
+
finalCompletionId?: string;
|
|
3589
|
+
}
|
|
3590
|
+
/**
|
|
3591
|
+
* A completed turn. Invariant: a `done` turn ALWAYS has a `nextUrl` (the
|
|
3592
|
+
* continuation pointer the server emits on the `done` event / persists as the
|
|
3593
|
+
* session's last completion). Encoded in the type so `state === "done"` narrows
|
|
3594
|
+
* `nextUrl` to `string` — restore/anchor logic can rely on it.
|
|
3595
|
+
*/
|
|
3596
|
+
export interface CodegenTurnDone extends CodegenTurnBase {
|
|
3597
|
+
state: "done";
|
|
3598
|
+
nextUrl: string;
|
|
3599
|
+
}
|
|
3600
|
+
/** A turn that is still running, or errored before completing. */
|
|
3601
|
+
export interface CodegenTurnPending extends CodegenTurnBase {
|
|
3602
|
+
state: "running" | "error";
|
|
3603
|
+
nextUrl: string | undefined;
|
|
3560
3604
|
}
|
|
3605
|
+
export type CodegenTurn = CodegenTurnDone | CodegenTurnPending;
|
|
3561
3606
|
export interface GetSessionTurnsResult {
|
|
3562
3607
|
turns: CodegenTurn[];
|
|
3563
3608
|
allIds: string[];
|
|
@@ -4636,3 +4681,4 @@ export interface SessionData {
|
|
|
4636
4681
|
numNormalUserMessages: number;
|
|
4637
4682
|
metadata?: Record<string, any>;
|
|
4638
4683
|
}
|
|
4684
|
+
export {};
|
package/src/codegen.js
CHANGED
|
@@ -1393,6 +1393,23 @@ export const IDEDiagnosticsToolInputSchema = z
|
|
|
1393
1393
|
}),
|
|
1394
1394
|
})
|
|
1395
1395
|
.meta({ title: "IDEDiagnosticsToolInput" });
|
|
1396
|
+
export const GetCodegenEventToolInputSchema = z
|
|
1397
|
+
.object({
|
|
1398
|
+
event_id: z.string().meta({
|
|
1399
|
+
description: "The codegen event ID to load (e.g. cgen-<eventId>).",
|
|
1400
|
+
}),
|
|
1401
|
+
})
|
|
1402
|
+
.meta({ title: "GetCodegenEventToolInput" });
|
|
1403
|
+
export const ListCodegenSessionEventsToolInputSchema = z
|
|
1404
|
+
.object({
|
|
1405
|
+
session_or_event_id: z.string().meta({
|
|
1406
|
+
description: "A session ID or codegen event ID. When an event ID is given, its session is resolved first.",
|
|
1407
|
+
}),
|
|
1408
|
+
limit: z.number().optional().meta({
|
|
1409
|
+
description: "Maximum number of events to return.",
|
|
1410
|
+
}),
|
|
1411
|
+
})
|
|
1412
|
+
.meta({ title: "ListCodegenSessionEventsToolInput" });
|
|
1396
1413
|
export const PullPrototypeToolInputSchema = z
|
|
1397
1414
|
.object({
|
|
1398
1415
|
url: z.string().meta({
|
|
@@ -1499,6 +1516,8 @@ export const CodeGenToolMapSchema = z.object({
|
|
|
1499
1516
|
ConnectMCP: ConnectMCPToolInputSchema,
|
|
1500
1517
|
EnsurePR: EnsurePRToolInputSchema,
|
|
1501
1518
|
GenerateDesignSystemAgentMd: GenerateDesignSystemAgentMdInputSchema,
|
|
1519
|
+
GetCodegenEvent: GetCodegenEventToolInputSchema,
|
|
1520
|
+
ListCodegenSessionEvents: ListCodegenSessionEventsToolInputSchema,
|
|
1502
1521
|
});
|
|
1503
1522
|
export const CodeGenToolsSchema = CodeGenToolMapSchema.keyof().meta({
|
|
1504
1523
|
title: "CodeGenTools",
|
package/src/design-systems.d.ts
CHANGED
|
@@ -35,10 +35,6 @@ export interface GenerateDesignSystemFormFields {
|
|
|
35
35
|
*/
|
|
36
36
|
export declare const GITHUB_REPO_URL_REGEX: RegExp;
|
|
37
37
|
export interface GenerateDesignSystemRequest extends GenerateDesignSystemFormFields {
|
|
38
|
-
/**
|
|
39
|
-
* Mixed list of uploaded files in any order. Sent as repeated
|
|
40
|
-
* `attachments` fields in the multipart body.
|
|
41
|
-
*/
|
|
42
38
|
attachments: File[];
|
|
43
39
|
}
|
|
44
40
|
export interface GenerateDesignSystemResponse {
|
|
@@ -58,18 +54,75 @@ export interface GenerateDesignSystemErrorResponse {
|
|
|
58
54
|
export declare const GENERATE_DESIGN_SYSTEM_MAX_FILE_BYTES: number;
|
|
59
55
|
export declare const GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS = 50;
|
|
60
56
|
export declare const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 1;
|
|
57
|
+
export declare const generateDesignSystemBodySchema: z.ZodObject<{
|
|
58
|
+
projectName: z.ZodOptional<z.ZodString>;
|
|
59
|
+
devToolsVersion: z.ZodOptional<z.ZodString>;
|
|
60
|
+
selection: z.ZodOptional<z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>>;
|
|
61
|
+
githubRepoUrl: z.ZodOptional<z.ZodString>;
|
|
62
|
+
connectedProjectId: z.ZodOptional<z.ZodString>;
|
|
63
|
+
}, z.core.$strip>;
|
|
64
|
+
export type GenerateDesignSystemBody = z.infer<typeof generateDesignSystemBodySchema>;
|
|
61
65
|
/**
|
|
62
|
-
*
|
|
63
|
-
* Files are parsed out of the body by multer before this schema runs.
|
|
66
|
+
* Signed-URL upload flow.
|
|
64
67
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
68
|
+
* Clients no longer post `.fig` bytes through the service (Cloud Run caps
|
|
69
|
+
* request bodies at 32 MiB). Instead they call `upload/start` to get a
|
|
70
|
+
* temporary signed resumable-upload URL per attachment, stream the bytes
|
|
71
|
+
* directly to GCS, then call `/generate` with the returned upload tokens.
|
|
72
|
+
* See `tech-specs/signed-url-figma-upload`.
|
|
67
73
|
*/
|
|
68
|
-
|
|
74
|
+
/** A single attachment the client declares when starting an upload. */
|
|
75
|
+
export declare const uploadStartAttachmentSchema: z.ZodObject<{
|
|
76
|
+
name: z.ZodString;
|
|
77
|
+
mimetype: z.ZodString;
|
|
78
|
+
declaredSize: z.ZodNumber;
|
|
79
|
+
}, z.core.$strip>;
|
|
80
|
+
export type UploadStartAttachment = z.infer<typeof uploadStartAttachmentSchema>;
|
|
81
|
+
/** Request body for `POST /design-systems/v1/upload/start`. */
|
|
82
|
+
export declare const uploadStartBodySchema: z.ZodObject<{
|
|
83
|
+
attachments: z.ZodArray<z.ZodObject<{
|
|
84
|
+
name: z.ZodString;
|
|
85
|
+
mimetype: z.ZodString;
|
|
86
|
+
declaredSize: z.ZodNumber;
|
|
87
|
+
}, z.core.$strip>>;
|
|
88
|
+
}, z.core.$strip>;
|
|
89
|
+
export type UploadStartBody = z.infer<typeof uploadStartBodySchema>;
|
|
90
|
+
/**
|
|
91
|
+
* Claims encoded inside a signed `uploadToken`. Bound to the GCS object at
|
|
92
|
+
* `upload/start` and verified at `/generate`, so a client cannot point the
|
|
93
|
+
* decode worker at an object it did not upload.
|
|
94
|
+
*/
|
|
95
|
+
export declare const uploadTokenPayloadSchema: z.ZodObject<{
|
|
96
|
+
jobId: z.ZodString;
|
|
97
|
+
gcsObject: z.ZodString;
|
|
98
|
+
ownerId: z.ZodString;
|
|
99
|
+
mimetype: z.ZodString;
|
|
100
|
+
originalName: z.ZodString;
|
|
101
|
+
idx: z.ZodNumber;
|
|
102
|
+
}, z.core.$strip>;
|
|
103
|
+
export type UploadTokenPayload = z.infer<typeof uploadTokenPayloadSchema>;
|
|
104
|
+
export interface UploadStartResponseItem {
|
|
105
|
+
idx: number;
|
|
106
|
+
/** Signed URL the client POSTs to (with `x-goog-resumable: start`) to begin the resumable upload. */
|
|
107
|
+
uploadUrl: string;
|
|
108
|
+
/** Opaque signed token the client passes back to `/generate`. */
|
|
109
|
+
uploadToken: string;
|
|
110
|
+
}
|
|
111
|
+
export interface UploadStartResponse {
|
|
112
|
+
jobId: string;
|
|
113
|
+
uploads: UploadStartResponseItem[];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Request body for the signed-URL variant of `POST /design-systems/v1/generate`.
|
|
117
|
+
* Same non-file fields as the legacy multipart form, plus the `uploads`
|
|
118
|
+
* tokens returned by `upload/start` (one per attachment).
|
|
119
|
+
*/
|
|
120
|
+
export declare const generateDesignSystemRequestSchema: z.ZodObject<{
|
|
69
121
|
projectName: z.ZodOptional<z.ZodString>;
|
|
70
122
|
devToolsVersion: z.ZodOptional<z.ZodString>;
|
|
71
123
|
selection: z.ZodOptional<z.ZodPreprocess<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>>;
|
|
72
124
|
githubRepoUrl: z.ZodOptional<z.ZodString>;
|
|
73
125
|
connectedProjectId: z.ZodOptional<z.ZodString>;
|
|
126
|
+
uploads: z.ZodArray<z.ZodString>;
|
|
74
127
|
}, z.core.$strip>;
|
|
75
|
-
export type
|
|
128
|
+
export type GenerateDesignSystemRequestBody = z.infer<typeof generateDesignSystemRequestSchema>;
|
package/src/design-systems.js
CHANGED
|
@@ -8,13 +8,6 @@ export const GITHUB_REPO_URL_REGEX = /^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[
|
|
|
8
8
|
export const GENERATE_DESIGN_SYSTEM_MAX_FILE_BYTES = 2 * 1024 * 1024 * 1024;
|
|
9
9
|
export const GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS = 50;
|
|
10
10
|
export const GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS = 1;
|
|
11
|
-
/**
|
|
12
|
-
* Validation for non-file fields posted alongside the multipart upload.
|
|
13
|
-
* Files are parsed out of the body by multer before this schema runs.
|
|
14
|
-
*
|
|
15
|
-
* The inferred type matches `GenerateDesignSystemFormFields` so the wire
|
|
16
|
-
* contract stays in lockstep with the shared type.
|
|
17
|
-
*/
|
|
18
11
|
export const generateDesignSystemBodySchema = z.object({
|
|
19
12
|
projectName: z.string().trim().min(1).max(200).optional(),
|
|
20
13
|
devToolsVersion: z.string().trim().min(1).max(64).optional(),
|
|
@@ -40,3 +33,54 @@ export const generateDesignSystemBodySchema = z.object({
|
|
|
40
33
|
.optional(),
|
|
41
34
|
connectedProjectId: z.string().trim().min(1).max(128).optional(),
|
|
42
35
|
});
|
|
36
|
+
/**
|
|
37
|
+
* Signed-URL upload flow.
|
|
38
|
+
*
|
|
39
|
+
* Clients no longer post `.fig` bytes through the service (Cloud Run caps
|
|
40
|
+
* request bodies at 32 MiB). Instead they call `upload/start` to get a
|
|
41
|
+
* temporary signed resumable-upload URL per attachment, stream the bytes
|
|
42
|
+
* directly to GCS, then call `/generate` with the returned upload tokens.
|
|
43
|
+
* See `tech-specs/signed-url-figma-upload`.
|
|
44
|
+
*/
|
|
45
|
+
/** A single attachment the client declares when starting an upload. */
|
|
46
|
+
export const uploadStartAttachmentSchema = z.object({
|
|
47
|
+
name: z.string().trim().min(1).max(512),
|
|
48
|
+
mimetype: z.string().trim().min(1).max(255),
|
|
49
|
+
declaredSize: z
|
|
50
|
+
.number()
|
|
51
|
+
.int()
|
|
52
|
+
.positive()
|
|
53
|
+
.max(GENERATE_DESIGN_SYSTEM_MAX_FILE_BYTES),
|
|
54
|
+
});
|
|
55
|
+
/** Request body for `POST /design-systems/v1/upload/start`. */
|
|
56
|
+
export const uploadStartBodySchema = z.object({
|
|
57
|
+
attachments: z
|
|
58
|
+
.array(uploadStartAttachmentSchema)
|
|
59
|
+
.min(GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS)
|
|
60
|
+
.max(GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS),
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* Claims encoded inside a signed `uploadToken`. Bound to the GCS object at
|
|
64
|
+
* `upload/start` and verified at `/generate`, so a client cannot point the
|
|
65
|
+
* decode worker at an object it did not upload.
|
|
66
|
+
*/
|
|
67
|
+
export const uploadTokenPayloadSchema = z.object({
|
|
68
|
+
jobId: z.string().min(1),
|
|
69
|
+
gcsObject: z.string().min(1),
|
|
70
|
+
ownerId: z.string().min(1),
|
|
71
|
+
mimetype: z.string().min(1),
|
|
72
|
+
originalName: z.string().min(1),
|
|
73
|
+
idx: z.number().int().nonnegative(),
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* Request body for the signed-URL variant of `POST /design-systems/v1/generate`.
|
|
77
|
+
* Same non-file fields as the legacy multipart form, plus the `uploads`
|
|
78
|
+
* tokens returned by `upload/start` (one per attachment).
|
|
79
|
+
*/
|
|
80
|
+
export const generateDesignSystemRequestSchema = z.object({
|
|
81
|
+
...generateDesignSystemBodySchema.shape,
|
|
82
|
+
uploads: z
|
|
83
|
+
.array(z.string().min(1))
|
|
84
|
+
.min(GENERATE_DESIGN_SYSTEM_MIN_ATTACHMENTS)
|
|
85
|
+
.max(GENERATE_DESIGN_SYSTEM_MAX_ATTACHMENTS),
|
|
86
|
+
});
|
package/src/events.d.ts
CHANGED
|
@@ -1039,6 +1039,8 @@ export type FigmaDecodeJobV1 = FusionEventVariant<"figma.decode.job", {
|
|
|
1039
1039
|
projectId: string;
|
|
1040
1040
|
projectName: string;
|
|
1041
1041
|
figmaName: string;
|
|
1042
|
+
/** Design-system record this job feeds; updated with the branch once created. */
|
|
1043
|
+
designSystemId?: string;
|
|
1042
1044
|
/**
|
|
1043
1045
|
* GCS object paths (relative to the configured bucket) of every attachment
|
|
1044
1046
|
* uploaded for this job. The worker streams these back down, runs
|
package/src/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./settings.js";
|
|
|
6
6
|
export * from "./mapping.js";
|
|
7
7
|
export * from "./common-schemas.js";
|
|
8
8
|
export * from "./codegen.js";
|
|
9
|
+
export * from "./codegen/investigation-context.js";
|
|
9
10
|
export * from "./diff-hunks.js";
|
|
10
11
|
export * from "./projects.js";
|
|
11
12
|
export * from "./repo-indexing.js";
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./settings.js";
|
|
|
6
6
|
export * from "./mapping.js";
|
|
7
7
|
export * from "./common-schemas.js";
|
|
8
8
|
export * from "./codegen.js";
|
|
9
|
+
export * from "./codegen/investigation-context.js";
|
|
9
10
|
export * from "./diff-hunks.js";
|
|
10
11
|
export * from "./projects.js";
|
|
11
12
|
export * from "./repo-indexing.js";
|
package/src/projects.d.ts
CHANGED
|
@@ -886,6 +886,11 @@ export interface ProjectDatabase {
|
|
|
886
886
|
createdAt: number;
|
|
887
887
|
createdBy: string;
|
|
888
888
|
lastActivityAt: number;
|
|
889
|
+
inactiveWarningSentAt?: number;
|
|
890
|
+
/** Set when the 60-day warning cannot be sent (no owner email); blocks further reclaim. */
|
|
891
|
+
inactiveWarningNoRecipientAt?: number;
|
|
892
|
+
inactiveSuspendedAt?: number;
|
|
893
|
+
inactiveDeletionScheduledAt?: number;
|
|
889
894
|
}
|
|
890
895
|
export interface BranchDatabase {
|
|
891
896
|
ownerId: string;
|
|
@@ -914,6 +919,13 @@ export type DatabaseDeletion = (DatabaseDeletionBase & {
|
|
|
914
919
|
neonBranchId?: never;
|
|
915
920
|
neonProjectAllBranches: true;
|
|
916
921
|
});
|
|
922
|
+
/** Grace period before scheduled Neon deletions run (reconciler + schedule APIs). */
|
|
923
|
+
export declare const NEON_DELETION_GRACE_PERIOD_MS: number;
|
|
924
|
+
/**
|
|
925
|
+
* `DatabaseDeletion.reason` for inactive-project reclaim. Must stay aligned
|
|
926
|
+
* across the reconciler (writes) and activity touch (queries/deletes).
|
|
927
|
+
*/
|
|
928
|
+
export declare const INACTIVE_PROJECT_RECLAIM_REASON: "inactive-project-reclaim";
|
|
917
929
|
/**
|
|
918
930
|
* Get the state of a branch, checking `state` first and falling back to `deleted` for backwards compatibility.
|
|
919
931
|
*/
|
package/src/projects.js
CHANGED
|
@@ -45,6 +45,13 @@ export const EXAMPLE_OR_STARTER_REPOS_URLS = EXAMPLE_OR_STARTER_REPOS.map((repo)
|
|
|
45
45
|
export const checkIsNewBranch = (branch) => {
|
|
46
46
|
return "projectId" in branch;
|
|
47
47
|
};
|
|
48
|
+
/** Grace period before scheduled Neon deletions run (reconciler + schedule APIs). */
|
|
49
|
+
export const NEON_DELETION_GRACE_PERIOD_MS = 30 * 24 * 60 * 60 * 1000;
|
|
50
|
+
/**
|
|
51
|
+
* `DatabaseDeletion.reason` for inactive-project reclaim. Must stay aligned
|
|
52
|
+
* across the reconciler (writes) and activity touch (queries/deletes).
|
|
53
|
+
*/
|
|
54
|
+
export const INACTIVE_PROJECT_RECLAIM_REASON = "inactive-project-reclaim";
|
|
48
55
|
/**
|
|
49
56
|
* Get the state of a branch, checking `state` first and falling back to `deleted` for backwards compatibility.
|
|
50
57
|
*/
|