veryfront 0.1.216 → 0.1.217

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.
Files changed (160) hide show
  1. package/esm/cli/commands/generate/integration-generator-helpers.d.ts +35 -0
  2. package/esm/cli/commands/generate/integration-generator-helpers.d.ts.map +1 -0
  3. package/esm/cli/commands/generate/integration-generator-helpers.js +123 -0
  4. package/esm/cli/commands/generate/integration-generator.d.ts +1 -2
  5. package/esm/cli/commands/generate/integration-generator.d.ts.map +1 -1
  6. package/esm/cli/commands/generate/integration-generator.js +1 -123
  7. package/esm/cli/commands/knowledge/command-helpers.d.ts +48 -0
  8. package/esm/cli/commands/knowledge/command-helpers.d.ts.map +1 -0
  9. package/esm/cli/commands/knowledge/command-helpers.js +216 -0
  10. package/esm/cli/commands/knowledge/command.d.ts +11 -16
  11. package/esm/cli/commands/knowledge/command.d.ts.map +1 -1
  12. package/esm/cli/commands/knowledge/command.js +25 -212
  13. package/esm/cli/mcp/remote-file-tool-helpers.d.ts +5 -0
  14. package/esm/cli/mcp/remote-file-tool-helpers.d.ts.map +1 -0
  15. package/esm/cli/mcp/remote-file-tool-helpers.js +22 -0
  16. package/esm/cli/mcp/remote-file-tools.d.ts.map +1 -1
  17. package/esm/cli/mcp/remote-file-tools.js +1 -22
  18. package/esm/cli/templates/integration-loader-helpers.d.ts +8 -0
  19. package/esm/cli/templates/integration-loader-helpers.d.ts.map +1 -0
  20. package/esm/cli/templates/integration-loader-helpers.js +29 -0
  21. package/esm/cli/templates/integration-loader.d.ts.map +1 -1
  22. package/esm/cli/templates/integration-loader.js +5 -21
  23. package/esm/cli/templates/manifest.d.ts +1 -0
  24. package/esm/cli/templates/manifest.js +2 -1
  25. package/esm/deno.js +2 -2
  26. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  27. package/esm/src/agent/runtime/index.js +48 -90
  28. package/esm/src/chat/ag-ui-helpers.d.ts +17 -0
  29. package/esm/src/chat/ag-ui-helpers.d.ts.map +1 -0
  30. package/esm/src/chat/ag-ui-helpers.js +111 -0
  31. package/esm/src/chat/ag-ui.d.ts.map +1 -1
  32. package/esm/src/chat/ag-ui.js +1 -111
  33. package/esm/src/config/schemas/config.schema.d.ts +1 -0
  34. package/esm/src/config/schemas/config.schema.d.ts.map +1 -1
  35. package/esm/src/config/schemas/config.schema.js +11 -0
  36. package/esm/src/config/schemas/index.d.ts +11 -1
  37. package/esm/src/config/schemas/index.d.ts.map +1 -1
  38. package/esm/src/errors/error-registry-helpers.d.ts +6 -0
  39. package/esm/src/errors/error-registry-helpers.d.ts.map +1 -0
  40. package/esm/src/errors/error-registry-helpers.js +9 -0
  41. package/esm/src/errors/error-registry.d.ts.map +1 -1
  42. package/esm/src/errors/error-registry.js +4 -3
  43. package/esm/src/extensions/factory-loader.d.ts +29 -0
  44. package/esm/src/extensions/factory-loader.d.ts.map +1 -0
  45. package/esm/src/extensions/factory-loader.js +63 -0
  46. package/esm/src/extensions/index.d.ts +16 -0
  47. package/esm/src/extensions/index.d.ts.map +1 -1
  48. package/esm/src/extensions/index.js +16 -0
  49. package/esm/src/extensions/orchestrate.d.ts +54 -0
  50. package/esm/src/extensions/orchestrate.d.ts.map +1 -0
  51. package/esm/src/extensions/orchestrate.js +116 -0
  52. package/esm/src/html/styles-builder/plugin-loader.d.ts.map +1 -1
  53. package/esm/src/html/styles-builder/plugin-loader.js +23 -0
  54. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts +29 -0
  55. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.d.ts.map +1 -0
  56. package/esm/src/html/styles-builder/tailwind-plugin-allowlist.js +48 -0
  57. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts +15 -0
  58. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.d.ts.map +1 -0
  59. package/esm/src/modules/react-loader/ssr-module-loader/loader-helpers.js +22 -0
  60. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts +0 -2
  61. package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
  62. package/esm/src/modules/react-loader/ssr-module-loader/loader.js +4 -24
  63. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts +19 -0
  64. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.d.ts.map +1 -0
  65. package/esm/src/platform/adapters/fs/veryfront/adapter-helpers.js +26 -0
  66. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  67. package/esm/src/platform/adapters/fs/veryfront/adapter.js +4 -20
  68. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts +1 -0
  69. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts.map +1 -1
  70. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.js +3 -0
  71. package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
  72. package/esm/src/platform/adapters/fs/veryfront/read-operations.js +4 -7
  73. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts +28 -0
  74. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.d.ts.map +1 -0
  75. package/esm/src/platform/adapters/fs/veryfront/websocket-manager-helpers.js +41 -0
  76. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -4
  77. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
  78. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +7 -40
  79. package/esm/src/provider/runtime-loader/tool-input-status.d.ts +17 -0
  80. package/esm/src/provider/runtime-loader/tool-input-status.d.ts.map +1 -0
  81. package/esm/src/provider/runtime-loader/tool-input-status.js +155 -0
  82. package/esm/src/provider/runtime-loader.d.ts +2 -3
  83. package/esm/src/provider/runtime-loader.d.ts.map +1 -1
  84. package/esm/src/provider/runtime-loader.js +2 -155
  85. package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts +8 -0
  86. package/esm/src/rendering/orchestrator/pipeline-helpers.d.ts.map +1 -0
  87. package/esm/src/rendering/orchestrator/pipeline-helpers.js +20 -0
  88. package/esm/src/rendering/orchestrator/pipeline.d.ts +0 -3
  89. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  90. package/esm/src/rendering/orchestrator/pipeline.js +4 -22
  91. package/esm/src/routing/api/module-loader/loader-helpers.d.ts +10 -0
  92. package/esm/src/routing/api/module-loader/loader-helpers.d.ts.map +1 -0
  93. package/esm/src/routing/api/module-loader/loader-helpers.js +62 -0
  94. package/esm/src/routing/api/module-loader/loader.d.ts +1 -1
  95. package/esm/src/routing/api/module-loader/loader.d.ts.map +1 -1
  96. package/esm/src/routing/api/module-loader/loader.js +2 -60
  97. package/esm/src/server/bootstrap.d.ts +22 -2
  98. package/esm/src/server/bootstrap.d.ts.map +1 -1
  99. package/esm/src/server/bootstrap.js +67 -5
  100. package/esm/src/server/dev-ui/manifest.d.ts +2 -0
  101. package/esm/src/server/dev-ui/manifest.js +3 -1
  102. package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
  103. package/esm/src/server/handlers/request/api/project-discovery.js +14 -8
  104. package/esm/src/server/production-server.js +1 -1
  105. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
  106. package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +2 -2
  107. package/esm/src/studio/bridge/bridge-bundle.generated.d.ts.map +1 -1
  108. package/esm/src/studio/bridge/bridge-bundle.generated.js +1 -1
  109. package/esm/src/tool/index.d.ts +1 -1
  110. package/esm/src/tool/index.d.ts.map +1 -1
  111. package/esm/src/tool/types.d.ts +20 -0
  112. package/esm/src/tool/types.d.ts.map +1 -1
  113. package/esm/src/utils/version-constant.d.ts +1 -1
  114. package/esm/src/utils/version-constant.js +1 -1
  115. package/package.json +1 -1
  116. package/src/cli/commands/generate/integration-generator-helpers.ts +185 -0
  117. package/src/cli/commands/generate/integration-generator.ts +12 -168
  118. package/src/cli/commands/knowledge/command-helpers.ts +295 -0
  119. package/src/cli/commands/knowledge/command.ts +33 -259
  120. package/src/cli/mcp/remote-file-tool-helpers.ts +27 -0
  121. package/src/cli/mcp/remote-file-tools.ts +6 -28
  122. package/src/cli/templates/integration-loader-helpers.ts +49 -0
  123. package/src/cli/templates/integration-loader.ts +10 -28
  124. package/src/cli/templates/manifest.js +2 -1
  125. package/src/deno.js +2 -2
  126. package/src/src/agent/runtime/index.ts +77 -94
  127. package/src/src/chat/ag-ui-helpers.ts +139 -0
  128. package/src/src/chat/ag-ui.ts +11 -139
  129. package/src/src/config/schemas/config.schema.ts +11 -0
  130. package/src/src/config/schemas/index.ts +15 -1
  131. package/src/src/errors/error-registry-helpers.ts +23 -0
  132. package/src/src/errors/error-registry.ts +8 -3
  133. package/src/src/extensions/factory-loader.ts +76 -0
  134. package/src/src/extensions/index.ts +20 -0
  135. package/src/src/extensions/orchestrate.ts +184 -0
  136. package/src/src/html/styles-builder/plugin-loader.ts +32 -0
  137. package/src/src/html/styles-builder/tailwind-plugin-allowlist.ts +51 -0
  138. package/src/src/modules/react-loader/ssr-module-loader/loader-helpers.ts +37 -0
  139. package/src/src/modules/react-loader/ssr-module-loader/loader.ts +8 -39
  140. package/src/src/platform/adapters/fs/veryfront/adapter-helpers.ts +43 -0
  141. package/src/src/platform/adapters/fs/veryfront/adapter.ts +8 -22
  142. package/src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts +4 -0
  143. package/src/src/platform/adapters/fs/veryfront/read-operations.ts +4 -7
  144. package/src/src/platform/adapters/fs/veryfront/websocket-manager-helpers.ts +73 -0
  145. package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +29 -44
  146. package/src/src/provider/runtime-loader/tool-input-status.ts +210 -0
  147. package/src/src/provider/runtime-loader.ts +6 -203
  148. package/src/src/rendering/orchestrator/pipeline-helpers.ts +35 -0
  149. package/src/src/rendering/orchestrator/pipeline.ts +8 -35
  150. package/src/src/routing/api/module-loader/loader-helpers.ts +68 -0
  151. package/src/src/routing/api/module-loader/loader.ts +8 -65
  152. package/src/src/server/bootstrap.ts +88 -7
  153. package/src/src/server/dev-ui/manifest.js +3 -1
  154. package/src/src/server/handlers/request/api/project-discovery.ts +19 -8
  155. package/src/src/server/production-server.ts +1 -1
  156. package/src/src/server/services/rsc/endpoints/rsc-bundles.generated.ts +2 -2
  157. package/src/src/studio/bridge/bridge-bundle.generated.ts +1 -1
  158. package/src/src/tool/index.ts +1 -0
  159. package/src/src/tool/types.ts +21 -0
  160. package/src/src/utils/version-constant.ts +1 -1
@@ -133,6 +133,51 @@ function getSkillActivationRequiredError(toolName: string): string {
133
133
  `Call "${LOAD_SKILL_TOOL_ID}" first to establish the active skill context.`;
134
134
  }
135
135
 
136
+ function warnLocalToolSkipping(agentId: string, modelId: string): void {
137
+ logger.warn(
138
+ `Agent "${agentId}" has tools configured but is using local model "${modelId}". ` +
139
+ "Local models don't support tool calling — tools will be skipped. " +
140
+ "Set VERYFRONT_API_TOKEN and VERYFRONT_PROJECT_SLUG, or configure " +
141
+ "OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY for full tool support.",
142
+ );
143
+ }
144
+
145
+ function createToolResultMessage(
146
+ toolCallId: string,
147
+ toolName: string,
148
+ result: unknown,
149
+ ): Message {
150
+ return {
151
+ id: `tool_${toolCallId}`,
152
+ role: "tool",
153
+ parts: [
154
+ {
155
+ type: "tool-result",
156
+ toolCallId,
157
+ toolName,
158
+ result,
159
+ },
160
+ ],
161
+ timestamp: Date.now(),
162
+ };
163
+ }
164
+
165
+ function createToolErrorMessage(toolCallId: string, toolName: string, error: string): Message {
166
+ return {
167
+ id: `tool_error_${toolCallId}`,
168
+ role: "tool",
169
+ parts: [
170
+ {
171
+ type: "tool-result",
172
+ toolCallId,
173
+ toolName,
174
+ result: { error },
175
+ },
176
+ ],
177
+ timestamp: Date.now(),
178
+ };
179
+ }
180
+
136
181
  export function collectFinalStreamToolResults(
137
182
  state: Pick<ChatStreamState, "toolResults">,
138
183
  ): Map<string, StreamingToolResult> {
@@ -671,12 +716,7 @@ export class AgentRuntime {
671
716
  // Local models can't reliably do function calling — skip tools gracefully.
672
717
  const isLocal = isLocalModelRuntime(languageModel);
673
718
  if (isLocal && this.config.tools) {
674
- logger.warn(
675
- `Agent "${this.id}" has tools configured but is using local model "${effectiveModel}". ` +
676
- "Local models don't support tool calling — tools will be skipped. " +
677
- "Set VERYFRONT_API_TOKEN and VERYFRONT_PROJECT_SLUG, or configure " +
678
- "OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY for full tool support.",
679
- );
719
+ warnLocalToolSkipping(this.id, effectiveModel);
680
720
  }
681
721
 
682
722
  // Request-scoped skill policy (not class-level mutable state)
@@ -769,21 +809,13 @@ export class AgentRuntime {
769
809
  const persistGeneratedToolResult = async (
770
810
  generatedToolResult: RuntimeGenerateToolResult,
771
811
  ): Promise<void> => {
772
- const toolResultMessage: Message = {
773
- id: `tool_${generatedToolResult.toolCallId}`,
774
- role: "tool",
775
- parts: [
776
- {
777
- type: "tool-result",
778
- toolCallId: generatedToolResult.toolCallId,
779
- toolName: generatedToolResult.toolName,
780
- result: generatedToolResult.isError === true
781
- ? { error: stringifyToolError(generatedToolResult.result) }
782
- : generatedToolResult.result,
783
- },
784
- ],
785
- timestamp: Date.now(),
786
- };
812
+ const toolResultMessage = createToolResultMessage(
813
+ generatedToolResult.toolCallId,
814
+ generatedToolResult.toolName,
815
+ generatedToolResult.isError === true
816
+ ? { error: stringifyToolError(generatedToolResult.result) }
817
+ : generatedToolResult.result,
818
+ );
787
819
  currentMessages.push(toolResultMessage);
788
820
  await this.memory.add(toolResultMessage);
789
821
  };
@@ -886,19 +918,11 @@ export class AgentRuntime {
886
918
  mustLoadSkillFirst = false;
887
919
  }
888
920
 
889
- const toolResultMessage: Message = {
890
- id: `tool_${tc.toolCallId}`,
891
- role: "tool",
892
- parts: [
893
- {
894
- type: "tool-result",
895
- toolCallId: tc.toolCallId,
896
- toolName: tc.toolName,
897
- result,
898
- },
899
- ],
900
- timestamp: Date.now(),
901
- };
921
+ const toolResultMessage = createToolResultMessage(
922
+ tc.toolCallId,
923
+ tc.toolName,
924
+ result,
925
+ );
902
926
  currentMessages.push(toolResultMessage);
903
927
  await this.memory.add(toolResultMessage);
904
928
  } catch (error) {
@@ -906,19 +930,11 @@ export class AgentRuntime {
906
930
  toolCall.error = error instanceof Error ? error.message : String(error);
907
931
  setSpanAttributes(toolSpan, { error: true, "error.message": toolCall.error });
908
932
 
909
- const errorMessage: Message = {
910
- id: `tool_error_${tc.toolCallId}`,
911
- role: "tool",
912
- parts: [
913
- {
914
- type: "tool-result",
915
- toolCallId: tc.toolCallId,
916
- toolName: tc.toolName,
917
- result: { error: toolCall.error },
918
- },
919
- ],
920
- timestamp: Date.now(),
921
- };
933
+ const errorMessage = createToolErrorMessage(
934
+ tc.toolCallId,
935
+ tc.toolName,
936
+ toolCall.error,
937
+ );
922
938
  currentMessages.push(errorMessage);
923
939
  await this.memory.add(errorMessage);
924
940
  }
@@ -980,12 +996,7 @@ export class AgentRuntime {
980
996
  // Local models can't reliably do function calling — skip tools gracefully.
981
997
  const isLocalStreaming = isLocalModelRuntime(languageModel);
982
998
  if (isLocalStreaming && this.config.tools) {
983
- logger.warn(
984
- `Agent "${this.id}" has tools configured but is using local model "${effectiveModel}". ` +
985
- "Local models don't support tool calling — tools will be skipped. " +
986
- "Set VERYFRONT_API_TOKEN and VERYFRONT_PROJECT_SLUG, or configure " +
987
- "OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY for full tool support.",
988
- );
999
+ warnLocalToolSkipping(this.id, effectiveModel);
989
1000
  }
990
1001
 
991
1002
  // Request-scoped skill policy (not class-level mutable state)
@@ -1100,21 +1111,13 @@ export class AgentRuntime {
1100
1111
  return;
1101
1112
  }
1102
1113
 
1103
- const toolResultMessage: Message = {
1104
- id: `tool_${toolResult.toolCallId}`,
1105
- role: "tool",
1106
- parts: [
1107
- {
1108
- type: "tool-result",
1109
- toolCallId: toolResult.toolCallId,
1110
- toolName: toolResult.toolName,
1111
- result: toolResult.error === undefined
1112
- ? toolResult.output
1113
- : { error: stringifyToolError(toolResult.error) },
1114
- },
1115
- ],
1116
- timestamp: Date.now(),
1117
- };
1114
+ const toolResultMessage = createToolResultMessage(
1115
+ toolResult.toolCallId,
1116
+ toolResult.toolName,
1117
+ toolResult.error === undefined
1118
+ ? toolResult.output
1119
+ : { error: stringifyToolError(toolResult.error) },
1120
+ );
1118
1121
  currentMessages.push(toolResultMessage);
1119
1122
  await this.memory.add(toolResultMessage);
1120
1123
  currentStepToolResults.set(
@@ -1270,19 +1273,7 @@ export class AgentRuntime {
1270
1273
  ...(dynamic ? { dynamic: true } : {}),
1271
1274
  });
1272
1275
 
1273
- const toolResultMessage: Message = {
1274
- id: `tool_${tc.id}`,
1275
- role: "tool",
1276
- parts: [
1277
- {
1278
- type: "tool-result",
1279
- toolCallId: tc.id,
1280
- toolName: tc.name,
1281
- result,
1282
- },
1283
- ],
1284
- timestamp: Date.now(),
1285
- };
1276
+ const toolResultMessage = createToolResultMessage(tc.id, tc.name, result);
1286
1277
  if (!currentStepToolResults.has(tc.id)) {
1287
1278
  currentMessages.push(toolResultMessage);
1288
1279
  await this.memory.add(toolResultMessage);
@@ -1343,19 +1334,11 @@ export class AgentRuntime {
1343
1334
  ...(dynamic ? { dynamic: true } : {}),
1344
1335
  });
1345
1336
 
1346
- const errorMessage: Message = {
1347
- id: `tool_error_${toolCall.id}`,
1348
- role: "tool",
1349
- parts: [
1350
- {
1351
- type: "tool-result",
1352
- toolCallId: toolCall.id,
1353
- toolName: toolCall.name,
1354
- result: { error: errorStr },
1355
- },
1356
- ],
1357
- timestamp: Date.now(),
1358
- };
1337
+ const errorMessage = createToolErrorMessage(
1338
+ toolCall.id,
1339
+ toolCall.name,
1340
+ errorStr,
1341
+ );
1359
1342
  currentMessages.push(errorMessage);
1360
1343
  await this.memory.add(errorMessage);
1361
1344
  }
@@ -0,0 +1,139 @@
1
+ import type { ChatFinishReason, ChatStreamEvent } from "./protocol.js";
2
+
3
+ type ParsedRenderableCustomChunk = Extract<
4
+ ChatStreamEvent,
5
+ { type: "source-url" | "source-document" | "file" }
6
+ >;
7
+
8
+ export function isRecord(value: unknown): value is Record<string, unknown> {
9
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10
+ }
11
+
12
+ export function normalizeNewlines(value: string): string {
13
+ return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
14
+ }
15
+
16
+ export function toRenderableCustomChunk(value: unknown): ParsedRenderableCustomChunk | null {
17
+ if (!isRecord(value) || typeof value.type !== "string") {
18
+ return null;
19
+ }
20
+
21
+ if (value.type === "source-url" && typeof value.url === "string") {
22
+ return {
23
+ type: "source-url",
24
+ sourceId: typeof value.sourceId === "string" && value.sourceId.length > 0
25
+ ? value.sourceId
26
+ : value.url,
27
+ url: value.url,
28
+ ...(typeof value.title === "string" ? { title: value.title } : {}),
29
+ };
30
+ }
31
+
32
+ if (
33
+ value.type === "source-document" &&
34
+ typeof value.sourceId === "string" &&
35
+ typeof value.mediaType === "string" &&
36
+ typeof value.title === "string"
37
+ ) {
38
+ return {
39
+ type: "source-document",
40
+ sourceId: value.sourceId,
41
+ mediaType: value.mediaType,
42
+ title: value.title,
43
+ ...(typeof value.filename === "string" ? { filename: value.filename } : {}),
44
+ };
45
+ }
46
+
47
+ if (
48
+ value.type === "file" && typeof value.url === "string" && typeof value.mediaType === "string"
49
+ ) {
50
+ return {
51
+ type: "file",
52
+ url: value.url,
53
+ mediaType: value.mediaType,
54
+ ...(typeof value.filename === "string" ? { filename: value.filename } : {}),
55
+ };
56
+ }
57
+
58
+ return null;
59
+ }
60
+
61
+ export function parseSerializedToolResult(value: unknown): unknown {
62
+ if (typeof value !== "string") {
63
+ return value;
64
+ }
65
+
66
+ const trimmed = value.trim();
67
+ if (
68
+ !trimmed.startsWith("{") &&
69
+ !trimmed.startsWith("[") &&
70
+ trimmed !== "null" &&
71
+ trimmed !== "true" &&
72
+ trimmed !== "false" &&
73
+ !/^[-]?\d+(\.\d+)?$/.test(trimmed)
74
+ ) {
75
+ return value;
76
+ }
77
+
78
+ try {
79
+ return JSON.parse(trimmed);
80
+ } catch {
81
+ return value;
82
+ }
83
+ }
84
+
85
+ export function formatToolErrorText(result: unknown): string {
86
+ if (typeof result === "string" && result.length > 0) {
87
+ return result;
88
+ }
89
+
90
+ if (isRecord(result)) {
91
+ if (typeof result.error === "string" && result.error.length > 0) {
92
+ return result.error;
93
+ }
94
+
95
+ if (typeof result.message === "string" && result.message.length > 0) {
96
+ return result.message;
97
+ }
98
+ }
99
+
100
+ return JSON.stringify(result ?? { error: "Tool execution failed" });
101
+ }
102
+
103
+ export function mapFinishReason(reason: string | undefined): ChatFinishReason | undefined {
104
+ if (!reason) return undefined;
105
+
106
+ switch (reason.trim().toLowerCase()) {
107
+ case "stop":
108
+ case "end_turn":
109
+ case "stop_sequence":
110
+ return "stop";
111
+ case "length":
112
+ case "max_tokens":
113
+ return "length";
114
+ case "tool_calls":
115
+ case "tool_use":
116
+ return "tool-calls";
117
+ case "content_filter":
118
+ case "content-filter":
119
+ return "content-filter";
120
+ case "error":
121
+ return "error";
122
+ default:
123
+ return "other";
124
+ }
125
+ }
126
+
127
+ export function splitSseFrames(value: string): { frames: string[]; remainder: string } {
128
+ const blocks = value.split("\n\n");
129
+ return {
130
+ frames: blocks.slice(0, -1),
131
+ remainder: blocks.at(-1) ?? "",
132
+ };
133
+ }
134
+
135
+ export function isCommentOnlySseFrame(raw: string): boolean {
136
+ return raw
137
+ .split("\n")
138
+ .every((line) => line.trim().length === 0 || line.trimStart().startsWith(":"));
139
+ }
@@ -1,6 +1,16 @@
1
1
  import "../../_dnt.polyfills.js";
2
2
  import { mergeToolInputDelta, parseToolInputObject } from "../agent/data-stream.js";
3
- import type { ChatFinishReason, ChatStreamEvent } from "./protocol.js";
3
+ import {
4
+ formatToolErrorText,
5
+ isCommentOnlySseFrame,
6
+ isRecord,
7
+ mapFinishReason,
8
+ normalizeNewlines,
9
+ parseSerializedToolResult,
10
+ splitSseFrames,
11
+ toRenderableCustomChunk,
12
+ } from "./ag-ui-helpers.js";
13
+ import type { ChatStreamEvent } from "./protocol.js";
4
14
  import { z } from "zod";
5
15
 
6
16
  type JsonPatchOperation = {
@@ -10,11 +20,6 @@ type JsonPatchOperation = {
10
20
  value?: unknown;
11
21
  };
12
22
 
13
- type ParsedRenderableCustomChunk = Extract<
14
- ChatStreamEvent,
15
- { type: "source-url" | "source-document" | "file" }
16
- >;
17
-
18
23
  type ToolCallState = {
19
24
  toolName: string;
20
25
  argsText: string;
@@ -292,125 +297,6 @@ export type AgUiSnapshotMessage = z.infer<typeof AgUiSnapshotMessageSchema>;
292
297
  export type AgUiWireEventName = z.infer<typeof AgUiWireEventNameSchema>;
293
298
  export type AgUiWireEvent = z.infer<typeof AgUiWireEventSchema>;
294
299
 
295
- function isRecord(value: unknown): value is Record<string, unknown> {
296
- return typeof value === "object" && value !== null && !Array.isArray(value);
297
- }
298
-
299
- function normalizeNewlines(value: string): string {
300
- return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
301
- }
302
-
303
- function toRenderableCustomChunk(value: unknown): ParsedRenderableCustomChunk | null {
304
- if (!isRecord(value) || typeof value.type !== "string") {
305
- return null;
306
- }
307
-
308
- if (value.type === "source-url" && typeof value.url === "string") {
309
- return {
310
- type: "source-url",
311
- sourceId: typeof value.sourceId === "string" && value.sourceId.length > 0
312
- ? value.sourceId
313
- : value.url,
314
- url: value.url,
315
- ...(typeof value.title === "string" ? { title: value.title } : {}),
316
- };
317
- }
318
-
319
- if (
320
- value.type === "source-document" &&
321
- typeof value.sourceId === "string" &&
322
- typeof value.mediaType === "string" &&
323
- typeof value.title === "string"
324
- ) {
325
- return {
326
- type: "source-document",
327
- sourceId: value.sourceId,
328
- mediaType: value.mediaType,
329
- title: value.title,
330
- ...(typeof value.filename === "string" ? { filename: value.filename } : {}),
331
- };
332
- }
333
-
334
- if (
335
- value.type === "file" && typeof value.url === "string" && typeof value.mediaType === "string"
336
- ) {
337
- return {
338
- type: "file",
339
- url: value.url,
340
- mediaType: value.mediaType,
341
- ...(typeof value.filename === "string" ? { filename: value.filename } : {}),
342
- };
343
- }
344
-
345
- return null;
346
- }
347
-
348
- function parseSerializedToolResult(value: unknown): unknown {
349
- if (typeof value !== "string") {
350
- return value;
351
- }
352
-
353
- const trimmed = value.trim();
354
- if (
355
- !trimmed.startsWith("{") &&
356
- !trimmed.startsWith("[") &&
357
- trimmed !== "null" &&
358
- trimmed !== "true" &&
359
- trimmed !== "false" &&
360
- !/^[-]?\d+(\.\d+)?$/.test(trimmed)
361
- ) {
362
- return value;
363
- }
364
-
365
- try {
366
- return JSON.parse(trimmed);
367
- } catch {
368
- return value;
369
- }
370
- }
371
-
372
- function formatToolErrorText(result: unknown): string {
373
- if (typeof result === "string" && result.length > 0) {
374
- return result;
375
- }
376
-
377
- if (isRecord(result)) {
378
- if (typeof result.error === "string" && result.error.length > 0) {
379
- return result.error;
380
- }
381
-
382
- if (typeof result.message === "string" && result.message.length > 0) {
383
- return result.message;
384
- }
385
- }
386
-
387
- return JSON.stringify(result ?? { error: "Tool execution failed" });
388
- }
389
-
390
- function mapFinishReason(reason: string | undefined): ChatFinishReason | undefined {
391
- if (!reason) return undefined;
392
-
393
- switch (reason.trim().toLowerCase()) {
394
- case "stop":
395
- case "end_turn":
396
- case "stop_sequence":
397
- return "stop";
398
- case "length":
399
- case "max_tokens":
400
- return "length";
401
- case "tool_calls":
402
- case "tool_use":
403
- return "tool-calls";
404
- case "content_filter":
405
- case "content-filter":
406
- return "content-filter";
407
- case "error":
408
- return "error";
409
- default:
410
- return "other";
411
- }
412
- }
413
-
414
300
  function getReasoningPartId(
415
301
  state: AgUiChatEventDecoderState,
416
302
  payload: { id?: string; messageId?: string },
@@ -440,20 +326,6 @@ function getReasoningPartId(
440
326
  return fallbackId;
441
327
  }
442
328
 
443
- function splitSseFrames(value: string): { frames: string[]; remainder: string } {
444
- const blocks = value.split("\n\n");
445
- return {
446
- frames: blocks.slice(0, -1),
447
- remainder: blocks.at(-1) ?? "",
448
- };
449
- }
450
-
451
- function isCommentOnlySseFrame(raw: string): boolean {
452
- return raw
453
- .split("\n")
454
- .every((line) => line.trim().length === 0 || line.trimStart().startsWith(":"));
455
- }
456
-
457
329
  function parseAgUiWireEvent(
458
330
  frame: ParsedSseEvent,
459
331
  input: {
@@ -491,6 +491,17 @@ export const veryfrontConfigSchema = z
491
491
  .optional(),
492
492
  )
493
493
  .optional(),
494
+ /**
495
+ * Extensions registered for this project.
496
+ *
497
+ * Each entry is either a fully-materialized `Extension` object or a
498
+ * disable directive `{ name, enabled: false }` that vetoes an extension
499
+ * discovered from a lower-priority source. The runtime type is
500
+ * tightened at the `veryfront/extensions` barrel — we keep this as
501
+ * `z.unknown()` here to avoid pulling the extensions module into the
502
+ * config layer (would introduce a circular import).
503
+ */
504
+ extensions: z.array(z.unknown()).optional(),
494
505
  /** OpenAPI documentation configuration */
495
506
  openapi: z
496
507
  .object({
@@ -7,7 +7,21 @@
7
7
  export {
8
8
  findUnknownTopLevelKeys,
9
9
  validateVeryfrontConfig,
10
- type VeryfrontConfig,
11
10
  type VeryfrontConfigInput,
12
11
  veryfrontConfigSchema,
13
12
  } from "./config.schema.js";
13
+
14
+ import type { VeryfrontConfig as BaseVeryfrontConfig } from "./config.schema.js";
15
+ // Type-only reference — keeps the config layer free of a runtime dependency
16
+ // on the extensions module. The schema stores `extensions` as `unknown[]`
17
+ // at runtime; this type assertion tightens it at the TS layer.
18
+ import type { ExtensionConfigEntry } from "../../extensions/types.js";
19
+
20
+ /**
21
+ * Project configuration. The underlying zod schema stores `extensions` as
22
+ * `unknown[]`; this tightened alias surfaces the expected
23
+ * `ExtensionConfigEntry[]` shape to TypeScript consumers.
24
+ */
25
+ export type VeryfrontConfig =
26
+ & Omit<BaseVeryfrontConfig, "extensions">
27
+ & { extensions?: ExtensionConfigEntry[] };
@@ -0,0 +1,23 @@
1
+ import type { ErrorCategory, RegisteredError } from "./types.js";
2
+
3
+ export type ErrorRegistryMap = Record<string, RegisteredError>;
4
+
5
+ export function getRegistryEntry<
6
+ Registry extends ErrorRegistryMap,
7
+ Slug extends keyof Registry,
8
+ >(registry: Registry, slug: Slug): Registry[Slug] {
9
+ return registry[slug];
10
+ }
11
+
12
+ export function getRegistryEntriesByCategory(
13
+ registry: ErrorRegistryMap,
14
+ category: ErrorCategory,
15
+ ): RegisteredError[] {
16
+ return Object.values(registry).filter((error) => error.category === category);
17
+ }
18
+
19
+ export function getRegistrySlugs<Registry extends ErrorRegistryMap>(
20
+ registry: Registry,
21
+ ): Array<keyof Registry> {
22
+ return Object.keys(registry) as Array<keyof Registry>;
23
+ }
@@ -1,3 +1,8 @@
1
+ import {
2
+ getRegistryEntriesByCategory,
3
+ getRegistryEntry,
4
+ getRegistrySlugs,
5
+ } from "./error-registry-helpers.js";
1
6
  import { defineError, type ErrorCategory } from "./types.js";
2
7
 
3
8
  // =============================================================================
@@ -801,19 +806,19 @@ export type ErrorSlug = keyof typeof ERROR_REGISTRY;
801
806
  * Get an error definition by slug
802
807
  */
803
808
  export function getErrorBySlug(slug: ErrorSlug) {
804
- return ERROR_REGISTRY[slug];
809
+ return getRegistryEntry(ERROR_REGISTRY, slug);
805
810
  }
806
811
 
807
812
  /**
808
813
  * Get all errors in a category
809
814
  */
810
815
  export function getErrorsByCategory(category: ErrorCategory) {
811
- return Object.values(ERROR_REGISTRY).filter((error) => error.category === category);
816
+ return getRegistryEntriesByCategory(ERROR_REGISTRY, category);
812
817
  }
813
818
 
814
819
  /**
815
820
  * Get all registered slugs
816
821
  */
817
822
  export function getAllSlugs(): ErrorSlug[] {
818
- return Object.keys(ERROR_REGISTRY) as ErrorSlug[];
823
+ return getRegistrySlugs(ERROR_REGISTRY) as ErrorSlug[];
819
824
  }