@builder.io/ai-utils 0.71.1 → 0.72.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@builder.io/ai-utils",
3
- "version": "0.71.1",
3
+ "version": "0.72.0",
4
4
  "description": "Builder.io AI utils",
5
5
  "files": [
6
6
  "src"
@@ -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";
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/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
  */